universal-mcp 0.1.5__py3-none-any.whl → 0.1.6__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/applications/e2b/README.md +37 -0
- universal_mcp/applications/firecrawl/README.md +45 -0
- universal_mcp/applications/github/README.md +13 -1
- universal_mcp/applications/google_docs/README.md +40 -0
- universal_mcp/applications/google_docs/app.py +93 -0
- universal_mcp/applications/google_drive/README.md +44 -0
- universal_mcp/applications/google_drive/app.py +280 -0
- universal_mcp/applications/google_mail/README.md +47 -0
- universal_mcp/applications/google_sheet/README.md +42 -0
- universal_mcp/applications/google_sheet/app.py +152 -0
- universal_mcp/applications/perplexity/README.md +37 -0
- universal_mcp/applications/reddit/README.md +45 -0
- universal_mcp/applications/resend/README.md +38 -0
- universal_mcp/applications/serp/README.md +37 -0
- universal_mcp/applications/tavily/README.md +38 -0
- universal_mcp/applications/zenquotes/README.md +37 -0
- universal_mcp/cli.py +3 -3
- universal_mcp/logger.py +7 -7
- universal_mcp/utils/dump_app_tools.py +68 -0
- {universal_mcp-0.1.5.dist-info → universal_mcp-0.1.6.dist-info}/METADATA +1 -1
- {universal_mcp-0.1.5.dist-info → universal_mcp-0.1.6.dist-info}/RECORD +23 -7
- {universal_mcp-0.1.5.dist-info → universal_mcp-0.1.6.dist-info}/WHEEL +0 -0
- {universal_mcp-0.1.5.dist-info → universal_mcp-0.1.6.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
# E2B MCP Server
|
3
|
+
|
4
|
+
An MCP Server for the E2B API.
|
5
|
+
|
6
|
+
## Supported Integrations
|
7
|
+
|
8
|
+
- AgentR
|
9
|
+
- API Key (Coming Soon)
|
10
|
+
- OAuth (Coming Soon)
|
11
|
+
|
12
|
+
## Tools
|
13
|
+
|
14
|
+
This is automatically generated from OpenAPI schema for the E2B API.
|
15
|
+
|
16
|
+
## Supported Integrations
|
17
|
+
|
18
|
+
This tool can be integrated with any service that supports HTTP requests.
|
19
|
+
|
20
|
+
## Tool List
|
21
|
+
|
22
|
+
| Tool | Description |
|
23
|
+
|------|-------------|
|
24
|
+
| execute_python_code | Executes Python code within a secure E2B cloud sandbox. |
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
- Login to AgentR
|
31
|
+
- Follow the quickstart guide to setup MCP Server for your client
|
32
|
+
- Visit Apps Store and enable the E2B app
|
33
|
+
- Restart the MCP Server
|
34
|
+
|
35
|
+
### Local Development
|
36
|
+
|
37
|
+
- Follow the README to test with the local MCP Server
|
@@ -0,0 +1,45 @@
|
|
1
|
+
|
2
|
+
# Firecrawl MCP Server
|
3
|
+
|
4
|
+
An MCP Server for the Firecrawl API.
|
5
|
+
|
6
|
+
## Supported Integrations
|
7
|
+
|
8
|
+
- AgentR
|
9
|
+
- API Key (Coming Soon)
|
10
|
+
- OAuth (Coming Soon)
|
11
|
+
|
12
|
+
## Tools
|
13
|
+
|
14
|
+
This is automatically generated from OpenAPI schema for the Firecrawl API.
|
15
|
+
|
16
|
+
## Supported Integrations
|
17
|
+
|
18
|
+
This tool can be integrated with any service that supports HTTP requests.
|
19
|
+
|
20
|
+
## Tool List
|
21
|
+
|
22
|
+
| Tool | Description |
|
23
|
+
|------|-------------|
|
24
|
+
| scrape_url | Scrapes a single URL using Firecrawl and returns the extracted data. |
|
25
|
+
| search | Performs a web search using Firecrawl's search capability. |
|
26
|
+
| start_crawl | Starts a crawl job for a given URL using Firecrawl. Returns the job ID immediately. |
|
27
|
+
| check_crawl_status | Checks the status of a previously initiated Firecrawl crawl job. |
|
28
|
+
| cancel_crawl | Cancels a currently running Firecrawl crawl job. |
|
29
|
+
| start_batch_scrape | Starts a batch scrape job for multiple URLs using Firecrawl. |
|
30
|
+
| check_batch_scrape_status | Checks the status of a previously initiated Firecrawl batch scrape job. |
|
31
|
+
| start_extract | Starts an extraction job for one or more URLs using Firecrawl. |
|
32
|
+
| check_extract_status | Checks the status of a previously initiated Firecrawl extraction job. |
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
## Usage
|
37
|
+
|
38
|
+
- Login to AgentR
|
39
|
+
- Follow the quickstart guide to setup MCP Server for your client
|
40
|
+
- Visit Apps Store and enable the Firecrawl app
|
41
|
+
- Restart the MCP Server
|
42
|
+
|
43
|
+
### Local Development
|
44
|
+
|
45
|
+
- Follow the README to test with the local MCP Server
|
@@ -19,7 +19,19 @@ This tool can be integrated with any service that supports HTTP requests.
|
|
19
19
|
|
20
20
|
## Tool List
|
21
21
|
|
22
|
-
|
22
|
+
| Tool | Description |
|
23
|
+
|------|-------------|
|
24
|
+
| create_issue | Create a new issue in a GitHub repository |
|
25
|
+
| create_pull_request | Create a new pull request for a GitHub repository |
|
26
|
+
| get_pull_request | Get a specific pull request for a GitHub repository |
|
27
|
+
| list_branches | List branches for a GitHub repository |
|
28
|
+
| list_commits | List recent commits for a GitHub repository |
|
29
|
+
| list_issues | List issues for a GitHub repository |
|
30
|
+
| list_pull_requests | List pull requests for a GitHub repository |
|
31
|
+
| list_repo_activities | List activities for a GitHub repository |
|
32
|
+
| star_repository | Star a GitHub repository |
|
33
|
+
| update_issue | Update an issue in a GitHub repository |
|
34
|
+
| validate | Function for validate |
|
23
35
|
|
24
36
|
|
25
37
|
|
@@ -0,0 +1,40 @@
|
|
1
|
+
|
2
|
+
# Google Docs MCP Server
|
3
|
+
|
4
|
+
An MCP Server for the Google Docs API.
|
5
|
+
|
6
|
+
## Supported Integrations
|
7
|
+
|
8
|
+
- AgentR
|
9
|
+
- API Key (Coming Soon)
|
10
|
+
- OAuth (Coming Soon)
|
11
|
+
|
12
|
+
## Tools
|
13
|
+
|
14
|
+
This is automatically generated from OpenAPI schema for the Google Docs API.
|
15
|
+
|
16
|
+
## Supported Integrations
|
17
|
+
|
18
|
+
This tool can be integrated with any service that supports HTTP requests.
|
19
|
+
|
20
|
+
## Tool List
|
21
|
+
|
22
|
+
| Tool | Description |
|
23
|
+
|------|-------------|
|
24
|
+
| add_content | Adds text content to an existing Google Document. |
|
25
|
+
| create_document | Creates a new blank Google Document with the specified title. |
|
26
|
+
| get_document | Gets the latest version of the specified document. |
|
27
|
+
| validate | Function for validate |
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
## Usage
|
32
|
+
|
33
|
+
- Login to AgentR
|
34
|
+
- Follow the quickstart guide to setup MCP Server for your client
|
35
|
+
- Visit Apps Store and enable the Google Docs app
|
36
|
+
- Restart the MCP Server
|
37
|
+
|
38
|
+
### Local Development
|
39
|
+
|
40
|
+
- Follow the README to test with the local MCP Server
|
@@ -0,0 +1,93 @@
|
|
1
|
+
from typing import Any
|
2
|
+
|
3
|
+
from loguru import logger
|
4
|
+
|
5
|
+
from universal_mcp.applications.application import APIApplication
|
6
|
+
from universal_mcp.exceptions import NotAuthorizedError
|
7
|
+
from universal_mcp.integrations import Integration
|
8
|
+
|
9
|
+
|
10
|
+
class GoogleDocsApp(APIApplication):
|
11
|
+
def __init__(self, integration: Integration) -> None:
|
12
|
+
super().__init__(name="google-docs", integration=integration)
|
13
|
+
self.base_api_url = "https://docs.googleapis.com/v1/documents"
|
14
|
+
|
15
|
+
def _get_headers(self):
|
16
|
+
if not self.integration:
|
17
|
+
raise ValueError("Integration not configured for GoogleDocsApp")
|
18
|
+
credentials = self.integration.get_credentials()
|
19
|
+
if not credentials:
|
20
|
+
logger.warning("No Google credentials found via integration.")
|
21
|
+
action = self.integration.authorize()
|
22
|
+
raise NotAuthorizedError(action)
|
23
|
+
if "headers" in credentials:
|
24
|
+
return credentials["headers"]
|
25
|
+
return {
|
26
|
+
"Authorization": f"Bearer {credentials['access_token']}",
|
27
|
+
"Content-Type": "application/json",
|
28
|
+
}
|
29
|
+
|
30
|
+
def create_document(self, title: str) -> dict[str, Any]:
|
31
|
+
"""
|
32
|
+
Creates a new blank Google Document with the specified title.
|
33
|
+
|
34
|
+
Args:
|
35
|
+
title: The title of the document to create
|
36
|
+
|
37
|
+
Returns:
|
38
|
+
The response from the Google Docs API
|
39
|
+
"""
|
40
|
+
url = self.base_api_url
|
41
|
+
document_data = {"title": title}
|
42
|
+
response = self._post(url, data=document_data)
|
43
|
+
response.raise_for_status()
|
44
|
+
return response.json()
|
45
|
+
|
46
|
+
def get_document(self, document_id: str) -> dict[str, Any]:
|
47
|
+
"""
|
48
|
+
Gets the latest version of the specified document.
|
49
|
+
|
50
|
+
Args:
|
51
|
+
document_id: The ID of the document to retrieve
|
52
|
+
|
53
|
+
Returns:
|
54
|
+
The response from the Google Docs API containing the document data
|
55
|
+
"""
|
56
|
+
url = f"{self.base_api_url}/{document_id}"
|
57
|
+
response = self._get(url)
|
58
|
+
return response.json()
|
59
|
+
|
60
|
+
def add_content(self, document_id: str, content: str, index: int = 1) -> dict[str, Any]:
|
61
|
+
"""
|
62
|
+
Adds text content to an existing Google Document.
|
63
|
+
|
64
|
+
Args:
|
65
|
+
document_id: The ID of the document to update
|
66
|
+
content: The text content to insert
|
67
|
+
index: The position at which to insert the text (default: 1, beginning of document)
|
68
|
+
|
69
|
+
Returns:
|
70
|
+
The response from the Google Docs API
|
71
|
+
"""
|
72
|
+
url = f"{self.base_api_url}/{document_id}:batchUpdate"
|
73
|
+
batch_update_data = {
|
74
|
+
"requests": [
|
75
|
+
{
|
76
|
+
"insertText": {
|
77
|
+
"location": {"index": index},
|
78
|
+
"text": content
|
79
|
+
}
|
80
|
+
}
|
81
|
+
]
|
82
|
+
}
|
83
|
+
|
84
|
+
response = self._post(url, data=batch_update_data)
|
85
|
+
response.raise_for_status()
|
86
|
+
return response.json()
|
87
|
+
|
88
|
+
def list_tools(self):
|
89
|
+
return [
|
90
|
+
self.create_document,
|
91
|
+
self.get_document,
|
92
|
+
self.add_content
|
93
|
+
]
|
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
# Google Drive MCP Server
|
3
|
+
|
4
|
+
An MCP Server for the Google Drive API.
|
5
|
+
|
6
|
+
## Supported Integrations
|
7
|
+
|
8
|
+
- AgentR
|
9
|
+
- API Key (Coming Soon)
|
10
|
+
- OAuth (Coming Soon)
|
11
|
+
|
12
|
+
## Tools
|
13
|
+
|
14
|
+
This is automatically generated from OpenAPI schema for the Google Drive API.
|
15
|
+
|
16
|
+
## Supported Integrations
|
17
|
+
|
18
|
+
This tool can be integrated with any service that supports HTTP requests.
|
19
|
+
|
20
|
+
## Tool List
|
21
|
+
|
22
|
+
| Tool | Description |
|
23
|
+
|------|-------------|
|
24
|
+
| get_drive_info | Get information about the user's Google Drive, including storage quota and user info. |
|
25
|
+
| list_files | List files in the user's Google Drive. |
|
26
|
+
| create_file_from_text | Create a new file from text content in Google Drive. |
|
27
|
+
| upload_a_file | Upload a file to Google Drive from a local binary file. |
|
28
|
+
| find_folder_id_by_name | Find a folder's ID by its name. |
|
29
|
+
| create_folder | Create a new folder in Google Drive. |
|
30
|
+
| get_file | Get metadata for a specific file. |
|
31
|
+
| delete_file | Delete a file from Google Drive. |
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
## Usage
|
36
|
+
|
37
|
+
- Login to AgentR
|
38
|
+
- Follow the quickstart guide to setup MCP Server for your client
|
39
|
+
- Visit Apps Store and enable the Google Drive app
|
40
|
+
- Restart the MCP Server
|
41
|
+
|
42
|
+
### Local Development
|
43
|
+
|
44
|
+
- Follow the README to test with the local MCP Server
|
@@ -0,0 +1,280 @@
|
|
1
|
+
from typing import Any
|
2
|
+
|
3
|
+
import httpx
|
4
|
+
from loguru import logger
|
5
|
+
|
6
|
+
from universal_mcp.applications.application import APIApplication
|
7
|
+
from universal_mcp.exceptions import NotAuthorizedError
|
8
|
+
from universal_mcp.integrations import Integration
|
9
|
+
|
10
|
+
|
11
|
+
class GoogleDriveApp(APIApplication):
|
12
|
+
"""
|
13
|
+
Application for interacting with Google Drive API.
|
14
|
+
Provides tools to manage files, folders, and access Drive information.
|
15
|
+
"""
|
16
|
+
|
17
|
+
def __init__(self, integration: Integration | None = None) -> None:
|
18
|
+
super().__init__(name="google-drive", integration=integration)
|
19
|
+
self.base_url = "https://www.googleapis.com/drive/v3"
|
20
|
+
|
21
|
+
def _get_headers(self):
|
22
|
+
if not self.integration:
|
23
|
+
raise ValueError("Integration not configured for GoogleDriveApp")
|
24
|
+
credentials = self.integration.get_credentials()
|
25
|
+
if not credentials:
|
26
|
+
logger.warning("No Google Drive credentials found via integration.")
|
27
|
+
action = self.integration.authorize()
|
28
|
+
raise NotAuthorizedError(action)
|
29
|
+
|
30
|
+
if "headers" in credentials:
|
31
|
+
return credentials["headers"]
|
32
|
+
return {
|
33
|
+
"Authorization": f"Bearer {credentials['access_token']}",
|
34
|
+
"Content-Type": "application/json",
|
35
|
+
}
|
36
|
+
|
37
|
+
def get_drive_info(self) -> dict[str, Any]:
|
38
|
+
"""
|
39
|
+
Get information about the user's Google Drive, including storage quota and user info.
|
40
|
+
|
41
|
+
Returns:
|
42
|
+
A dictionary containing information about the user's Drive.
|
43
|
+
Includes details like storage quota, user information, and features.
|
44
|
+
"""
|
45
|
+
url = f"{self.base_url}/about"
|
46
|
+
params = {"fields": "storageQuota,user"}
|
47
|
+
|
48
|
+
response = self._get(url, params=params)
|
49
|
+
return response.json()
|
50
|
+
|
51
|
+
def list_files(self, page_size: int = 10, query: str = None, order_by: str = None) -> dict[str, Any]:
|
52
|
+
"""
|
53
|
+
List files in the user's Google Drive.
|
54
|
+
|
55
|
+
Args:
|
56
|
+
page_size: Maximum number of files to return (default: 10)
|
57
|
+
query: Search query using Google Drive query syntax (e.g., "mimeType='image/jpeg'")
|
58
|
+
order_by: Field to sort by (e.g., "modifiedTime desc")
|
59
|
+
|
60
|
+
Returns:
|
61
|
+
A dictionary containing the list of files and possibly a nextPageToken.
|
62
|
+
"""
|
63
|
+
url = f"{self.base_url}/files"
|
64
|
+
params = {
|
65
|
+
"pageSize": page_size,
|
66
|
+
}
|
67
|
+
|
68
|
+
if query:
|
69
|
+
params["q"] = query
|
70
|
+
|
71
|
+
if order_by:
|
72
|
+
params["orderBy"] = order_by
|
73
|
+
|
74
|
+
response = self._get(url, params=params)
|
75
|
+
response.raise_for_status()
|
76
|
+
return response.json()
|
77
|
+
|
78
|
+
|
79
|
+
def get_file(self, file_id: str) -> dict[str, Any]:
|
80
|
+
"""
|
81
|
+
Get metadata for a specific file.
|
82
|
+
|
83
|
+
Args:
|
84
|
+
file_id: The ID of the file to retrieve
|
85
|
+
|
86
|
+
Returns:
|
87
|
+
A dictionary containing the file's metadata.
|
88
|
+
"""
|
89
|
+
url = f"{self.base_url}/files/{file_id}"
|
90
|
+
response = self._get(url)
|
91
|
+
return response.json()
|
92
|
+
|
93
|
+
def delete_file(self, file_id: str) -> dict[str, Any]:
|
94
|
+
"""
|
95
|
+
Delete a file from Google Drive.
|
96
|
+
|
97
|
+
Args:
|
98
|
+
file_id: The ID of the file to delete
|
99
|
+
|
100
|
+
Returns:
|
101
|
+
A simple success message dictionary
|
102
|
+
"""
|
103
|
+
url = f"{self.base_url}/files/{file_id}"
|
104
|
+
try:
|
105
|
+
self._delete(url)
|
106
|
+
return {"message": "File deleted successfully"}
|
107
|
+
except Exception as e:
|
108
|
+
return {"error": str(e)}
|
109
|
+
|
110
|
+
def create_file_from_text(
|
111
|
+
self,
|
112
|
+
file_name: str,
|
113
|
+
text_content: str,
|
114
|
+
parent_id: str = None,
|
115
|
+
mime_type: str = "text/plain"
|
116
|
+
) -> dict[str, Any]:
|
117
|
+
"""
|
118
|
+
Create a new file from text content in Google Drive.
|
119
|
+
|
120
|
+
Args:
|
121
|
+
file_name: Name of the file to create on Google Drive
|
122
|
+
text_content: Plain text content to be written to the file
|
123
|
+
parent_id: ID of the parent folder to create the file in (optional)
|
124
|
+
mime_type: MIME type of the file (default: text/plain)
|
125
|
+
|
126
|
+
Returns:
|
127
|
+
A dictionary containing the created file's metadata.
|
128
|
+
"""
|
129
|
+
metadata = {
|
130
|
+
"name": file_name,
|
131
|
+
"mimeType": mime_type
|
132
|
+
}
|
133
|
+
|
134
|
+
if parent_id:
|
135
|
+
metadata["parents"] = [parent_id]
|
136
|
+
|
137
|
+
create_url = f"{self.base_url}/files"
|
138
|
+
create_response = self._post(create_url, data=metadata)
|
139
|
+
file_data = create_response.json()
|
140
|
+
file_id = file_data.get("id")
|
141
|
+
|
142
|
+
# Step 2: Update the file with text content
|
143
|
+
upload_url = f"https://www.googleapis.com/upload/drive/v3/files/{file_id}?uploadType=media"
|
144
|
+
upload_headers = self._get_headers()
|
145
|
+
upload_headers["Content-Type"] = f"{mime_type}; charset=utf-8"
|
146
|
+
|
147
|
+
upload_response = httpx.patch(
|
148
|
+
upload_url,
|
149
|
+
headers=upload_headers,
|
150
|
+
content=text_content.encode("utf-8")
|
151
|
+
)
|
152
|
+
upload_response.raise_for_status()
|
153
|
+
|
154
|
+
response_data = upload_response.json()
|
155
|
+
return response_data
|
156
|
+
|
157
|
+
def find_folder_id_by_name(self, folder_name: str) -> str | None:
|
158
|
+
"""
|
159
|
+
Find a folder's ID by its name.
|
160
|
+
|
161
|
+
Args:
|
162
|
+
folder_name: The name of the folder to find
|
163
|
+
|
164
|
+
Returns:
|
165
|
+
The folder ID if found, None otherwise
|
166
|
+
"""
|
167
|
+
query = f"mimeType='application/vnd.google-apps.folder' and name='{folder_name}' and trashed=false"
|
168
|
+
try:
|
169
|
+
response = self._get(
|
170
|
+
f"{self.base_url}/files",
|
171
|
+
params={"q": query, "fields": "files(id,name)"}
|
172
|
+
)
|
173
|
+
files = response.json().get("files", [])
|
174
|
+
return files[0]["id"] if files else None
|
175
|
+
except Exception as e:
|
176
|
+
logger.error(f"Error finding folder ID by name: {e}")
|
177
|
+
return None
|
178
|
+
|
179
|
+
def create_folder(self, folder_name: str, parent_id: str = None) -> dict[str, Any]:
|
180
|
+
"""
|
181
|
+
Create a new folder in Google Drive.
|
182
|
+
|
183
|
+
Args:
|
184
|
+
folder_name: Name of the folder to create
|
185
|
+
parent_id: ID of the parent folder (optional). Can be an actual folder ID
|
186
|
+
or a folder name that will be looked up.
|
187
|
+
|
188
|
+
Returns:
|
189
|
+
A dictionary containing the created folder's metadata.
|
190
|
+
"""
|
191
|
+
import re
|
192
|
+
|
193
|
+
metadata = {
|
194
|
+
"name": folder_name,
|
195
|
+
"mimeType": "application/vnd.google-apps.folder"
|
196
|
+
}
|
197
|
+
|
198
|
+
if parent_id:
|
199
|
+
if not re.match(r"^[a-zA-Z0-9_-]{28,33}$", parent_id):
|
200
|
+
found_id = self.find_folder_id_by_name(parent_id)
|
201
|
+
if found_id:
|
202
|
+
metadata["parents"] = [found_id]
|
203
|
+
else:
|
204
|
+
raise ValueError(f"Could not find parent folder with name: {parent_id}")
|
205
|
+
else:
|
206
|
+
metadata["parents"] = [parent_id]
|
207
|
+
|
208
|
+
url = f"{self.base_url}/files"
|
209
|
+
params = {"supportsAllDrives": "true"}
|
210
|
+
|
211
|
+
response = self._post(url, data=metadata, params=params)
|
212
|
+
return response.json()
|
213
|
+
|
214
|
+
def upload_a_file(
|
215
|
+
self,
|
216
|
+
file_name: str,
|
217
|
+
file_path: str,
|
218
|
+
parent_id: str = None,
|
219
|
+
mime_type: str = None
|
220
|
+
) -> dict[str, Any]:
|
221
|
+
"""
|
222
|
+
Upload a file to Google Drive from a local binary file.
|
223
|
+
|
224
|
+
This method is suitable for files under 5MB in size. It automatically
|
225
|
+
reads the file from the provided path.
|
226
|
+
|
227
|
+
Args:
|
228
|
+
file_name: Name to give the file on Google Drive
|
229
|
+
file_path: Path to the local file to upload
|
230
|
+
parent_id: ID of the parent folder to create the file in (optional)
|
231
|
+
mime_type: MIME type of the file
|
232
|
+
Examples: 'image/jpeg', 'image/png', 'application/pdf', 'text/csv'
|
233
|
+
|
234
|
+
Returns:
|
235
|
+
A dictionary containing the created file's metadata.
|
236
|
+
"""
|
237
|
+
|
238
|
+
|
239
|
+
metadata = {
|
240
|
+
"name": file_name,
|
241
|
+
"mimeType": mime_type
|
242
|
+
}
|
243
|
+
|
244
|
+
if parent_id:
|
245
|
+
metadata["parents"] = [parent_id]
|
246
|
+
|
247
|
+
create_url = f"{self.base_url}/files"
|
248
|
+
create_response = self._post(create_url, data=metadata)
|
249
|
+
file_data = create_response.json()
|
250
|
+
file_id = file_data.get("id")
|
251
|
+
|
252
|
+
with open(file_path, 'rb') as file_content:
|
253
|
+
binary_content = file_content.read()
|
254
|
+
|
255
|
+
upload_url = f"https://www.googleapis.com/upload/drive/v3/files/{file_id}?uploadType=media"
|
256
|
+
upload_headers = self._get_headers()
|
257
|
+
upload_headers["Content-Type"] = mime_type
|
258
|
+
|
259
|
+
upload_response = httpx.patch(
|
260
|
+
upload_url,
|
261
|
+
headers=upload_headers,
|
262
|
+
content=binary_content
|
263
|
+
)
|
264
|
+
upload_response.raise_for_status()
|
265
|
+
|
266
|
+
response_data = upload_response.json()
|
267
|
+
return response_data
|
268
|
+
|
269
|
+
def list_tools(self):
|
270
|
+
"""Returns a list of methods exposed as tools."""
|
271
|
+
return [
|
272
|
+
self.get_drive_info,
|
273
|
+
self.list_files,
|
274
|
+
self.create_file_from_text,
|
275
|
+
self.upload_a_file,
|
276
|
+
self.find_folder_id_by_name,
|
277
|
+
self.create_folder,
|
278
|
+
self.get_file,
|
279
|
+
self.delete_file,
|
280
|
+
]
|
@@ -0,0 +1,47 @@
|
|
1
|
+
|
2
|
+
# Google Mail MCP Server
|
3
|
+
|
4
|
+
An MCP Server for the Google Mail API.
|
5
|
+
|
6
|
+
## Supported Integrations
|
7
|
+
|
8
|
+
- AgentR
|
9
|
+
- API Key (Coming Soon)
|
10
|
+
- OAuth (Coming Soon)
|
11
|
+
|
12
|
+
## Tools
|
13
|
+
|
14
|
+
This is automatically generated from OpenAPI schema for the Google Mail API.
|
15
|
+
|
16
|
+
## Supported Integrations
|
17
|
+
|
18
|
+
This tool can be integrated with any service that supports HTTP requests.
|
19
|
+
|
20
|
+
## Tool List
|
21
|
+
|
22
|
+
| Tool | Description |
|
23
|
+
|------|-------------|
|
24
|
+
| create_draft | Create a draft email |
|
25
|
+
| create_label | Create a new Gmail label |
|
26
|
+
| get_draft | Get a specific draft email by ID |
|
27
|
+
| get_message | Get a specific email message by ID |
|
28
|
+
| get_profile | Retrieve the user's Gmail profile information. |
|
29
|
+
| list_drafts | List drafts in the user's mailbox |
|
30
|
+
| list_labels | List all labels in the user's Gmail account |
|
31
|
+
| list_messages | List messages in the user's mailbox |
|
32
|
+
| send_draft | Send an existing draft email |
|
33
|
+
| send_email | Send an email |
|
34
|
+
| validate | Function for validate |
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
## Usage
|
39
|
+
|
40
|
+
- Login to AgentR
|
41
|
+
- Follow the quickstart guide to setup MCP Server for your client
|
42
|
+
- Visit Apps Store and enable the Google Mail app
|
43
|
+
- Restart the MCP Server
|
44
|
+
|
45
|
+
### Local Development
|
46
|
+
|
47
|
+
- Follow the README to test with the local MCP Server
|
@@ -0,0 +1,42 @@
|
|
1
|
+
|
2
|
+
# Google Sheet MCP Server
|
3
|
+
|
4
|
+
An MCP Server for the Google Sheet API.
|
5
|
+
|
6
|
+
## Supported Integrations
|
7
|
+
|
8
|
+
- AgentR
|
9
|
+
- API Key (Coming Soon)
|
10
|
+
- OAuth (Coming Soon)
|
11
|
+
|
12
|
+
## Tools
|
13
|
+
|
14
|
+
This is automatically generated from OpenAPI schema for the Google Sheet API.
|
15
|
+
|
16
|
+
## Supported Integrations
|
17
|
+
|
18
|
+
This tool can be integrated with any service that supports HTTP requests.
|
19
|
+
|
20
|
+
## Tool List
|
21
|
+
|
22
|
+
| Tool | Description |
|
23
|
+
|------|-------------|
|
24
|
+
| create_spreadsheet | Creates a new blank Google Spreadsheet with the specified title. |
|
25
|
+
| get_spreadsheet | Returns the spreadsheet details. |
|
26
|
+
| batch_get_values | Returns one or more ranges of values from a spreadsheet. |
|
27
|
+
| clear_values | Clears values from a spreadsheet. Only values are cleared -- all other properties
|
28
|
+
of the cell (such as formatting, data validation, etc.) are kept. |
|
29
|
+
| update_values | Sets values in a range of a spreadsheet. |
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
## Usage
|
34
|
+
|
35
|
+
- Login to AgentR
|
36
|
+
- Follow the quickstart guide to setup MCP Server for your client
|
37
|
+
- Visit Apps Store and enable the Google Sheet app
|
38
|
+
- Restart the MCP Server
|
39
|
+
|
40
|
+
### Local Development
|
41
|
+
|
42
|
+
- Follow the README to test with the local MCP Server
|
@@ -0,0 +1,152 @@
|
|
1
|
+
from typing import Any
|
2
|
+
|
3
|
+
from loguru import logger
|
4
|
+
|
5
|
+
from universal_mcp.applications.application import APIApplication
|
6
|
+
from universal_mcp.exceptions import NotAuthorizedError
|
7
|
+
from universal_mcp.integrations import Integration
|
8
|
+
|
9
|
+
|
10
|
+
class GoogleSheetApp(APIApplication):
|
11
|
+
"""
|
12
|
+
Application for interacting with Google Sheets API.
|
13
|
+
Provides tools to create and manage Google Spreadsheets.
|
14
|
+
"""
|
15
|
+
|
16
|
+
def __init__(self, integration: Integration | None = None) -> None:
|
17
|
+
super().__init__(name="google-sheet", integration=integration)
|
18
|
+
self.base_api_url = "https://sheets.googleapis.com/v4/spreadsheets"
|
19
|
+
|
20
|
+
def _get_headers(self):
|
21
|
+
if not self.integration:
|
22
|
+
raise ValueError("Integration not configured for GoogleSheetsApp")
|
23
|
+
credentials = self.integration.get_credentials()
|
24
|
+
if not credentials:
|
25
|
+
logger.warning("No Google credentials found via integration.")
|
26
|
+
action = self.integration.authorize()
|
27
|
+
raise NotAuthorizedError(action)
|
28
|
+
if "headers" in credentials:
|
29
|
+
return credentials["headers"]
|
30
|
+
return {
|
31
|
+
"Authorization": f"Bearer {credentials['access_token']}",
|
32
|
+
"Content-Type": "application/json",
|
33
|
+
}
|
34
|
+
|
35
|
+
def create_spreadsheet(self, title: str) -> dict[str, Any]:
|
36
|
+
"""
|
37
|
+
Creates a new blank Google Spreadsheet with the specified title.
|
38
|
+
|
39
|
+
Args:
|
40
|
+
title: The title of the spreadsheet to create required , which is provided by the user
|
41
|
+
|
42
|
+
Returns:
|
43
|
+
The response from the Google Sheets API
|
44
|
+
"""
|
45
|
+
url = self.base_api_url
|
46
|
+
spreadsheet_data = {
|
47
|
+
"properties": {
|
48
|
+
"title": title
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
response = self._post(url, data=spreadsheet_data)
|
53
|
+
return response.json()
|
54
|
+
|
55
|
+
def get_spreadsheet(self, spreadsheet_id: str) -> dict[str, Any]:
|
56
|
+
"""
|
57
|
+
Returns the spreadsheet details.
|
58
|
+
|
59
|
+
Args:
|
60
|
+
spreadsheet_id: The ID of the spreadsheet to retrieve
|
61
|
+
|
62
|
+
Returns:
|
63
|
+
The response from the Google Sheets API containing the spreadsheet data and details
|
64
|
+
"""
|
65
|
+
url = f"{self.base_api_url}/{spreadsheet_id}"
|
66
|
+
response = self._get(url)
|
67
|
+
return response.json()
|
68
|
+
|
69
|
+
def batch_get_values(self, spreadsheet_id: str, ranges: list[str] = None) -> dict[str, Any]:
|
70
|
+
"""
|
71
|
+
Returns one or more ranges of values from a spreadsheet.
|
72
|
+
|
73
|
+
Args:
|
74
|
+
spreadsheet_id: The ID of the spreadsheet to retrieve values from
|
75
|
+
ranges: Optional list of A1 notation or R1C1 notation ranges to retrieve values from
|
76
|
+
(e.g. ['Sheet1!A1:B2', 'Sheet2!C3:D4'])
|
77
|
+
|
78
|
+
Returns:
|
79
|
+
The response from the Google Sheets API containing the requested values
|
80
|
+
"""
|
81
|
+
url = f"{self.base_api_url}/{spreadsheet_id}/values:batchGet"
|
82
|
+
|
83
|
+
params = {}
|
84
|
+
if ranges:
|
85
|
+
params["ranges"] = ranges
|
86
|
+
|
87
|
+
response = self._get(url, params=params)
|
88
|
+
return response.json()
|
89
|
+
|
90
|
+
def clear_values(self, spreadsheet_id: str, range: str) -> dict[str, Any]:
|
91
|
+
"""
|
92
|
+
Clears values from a spreadsheet. Only values are cleared -- all other properties
|
93
|
+
of the cell (such as formatting, data validation, etc.) are kept.
|
94
|
+
|
95
|
+
Args:
|
96
|
+
spreadsheet_id: The ID of the spreadsheet to update
|
97
|
+
range: The A1 notation or R1C1 notation of the values to clear
|
98
|
+
(e.g. 'Sheet1!A1:B2')
|
99
|
+
|
100
|
+
Returns:
|
101
|
+
The response from the Google Sheets API
|
102
|
+
"""
|
103
|
+
url = f"{self.base_api_url}/{spreadsheet_id}/values/{range}:clear"
|
104
|
+
|
105
|
+
response = self._post(url, data={})
|
106
|
+
return response.json()
|
107
|
+
|
108
|
+
def update_values(
|
109
|
+
self,
|
110
|
+
spreadsheet_id: str,
|
111
|
+
range: str,
|
112
|
+
values: list[list[Any]],
|
113
|
+
value_input_option: str = "RAW"
|
114
|
+
) -> dict[str, Any]:
|
115
|
+
"""
|
116
|
+
Sets values in a range of a spreadsheet.
|
117
|
+
|
118
|
+
Args:
|
119
|
+
spreadsheet_id: The ID of the spreadsheet to update
|
120
|
+
range: The A1 notation of the values to update (e.g. 'Sheet1!A1:B2')
|
121
|
+
values: The data to write, as a list of lists (rows of values)
|
122
|
+
value_input_option: How the input data should be interpreted.
|
123
|
+
Accepted values are:
|
124
|
+
- "RAW": The values will be stored as-is
|
125
|
+
- "USER_ENTERED": The values will be parsed as if the user typed them into the UI
|
126
|
+
|
127
|
+
Returns:
|
128
|
+
The response from the Google Sheets API
|
129
|
+
"""
|
130
|
+
url = f"{self.base_api_url}/{spreadsheet_id}/values/{range}"
|
131
|
+
|
132
|
+
params = {
|
133
|
+
"valueInputOption": value_input_option
|
134
|
+
}
|
135
|
+
|
136
|
+
data = {
|
137
|
+
"range": range,
|
138
|
+
"values": values
|
139
|
+
}
|
140
|
+
|
141
|
+
response = self._put(url, data=data, params=params)
|
142
|
+
return response.json()
|
143
|
+
|
144
|
+
def list_tools(self):
|
145
|
+
"""Returns a list of methods exposed as tools."""
|
146
|
+
return [
|
147
|
+
self.create_spreadsheet,
|
148
|
+
self.get_spreadsheet,
|
149
|
+
self.batch_get_values,
|
150
|
+
self.clear_values,
|
151
|
+
self.update_values
|
152
|
+
]
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
# Perplexity MCP Server
|
3
|
+
|
4
|
+
An MCP Server for the Perplexity API.
|
5
|
+
|
6
|
+
## Supported Integrations
|
7
|
+
|
8
|
+
- AgentR
|
9
|
+
- API Key (Coming Soon)
|
10
|
+
- OAuth (Coming Soon)
|
11
|
+
|
12
|
+
## Tools
|
13
|
+
|
14
|
+
This is automatically generated from OpenAPI schema for the Perplexity API.
|
15
|
+
|
16
|
+
## Supported Integrations
|
17
|
+
|
18
|
+
This tool can be integrated with any service that supports HTTP requests.
|
19
|
+
|
20
|
+
## Tool List
|
21
|
+
|
22
|
+
| Tool | Description |
|
23
|
+
|------|-------------|
|
24
|
+
| chat | Sends a query to a Perplexity Sonar online model and returns the response. |
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
- Login to AgentR
|
31
|
+
- Follow the quickstart guide to setup MCP Server for your client
|
32
|
+
- Visit Apps Store and enable the Perplexity app
|
33
|
+
- Restart the MCP Server
|
34
|
+
|
35
|
+
### Local Development
|
36
|
+
|
37
|
+
- Follow the README to test with the local MCP Server
|
@@ -0,0 +1,45 @@
|
|
1
|
+
|
2
|
+
# Reddit MCP Server
|
3
|
+
|
4
|
+
An MCP Server for the Reddit API.
|
5
|
+
|
6
|
+
## Supported Integrations
|
7
|
+
|
8
|
+
- AgentR
|
9
|
+
- API Key (Coming Soon)
|
10
|
+
- OAuth (Coming Soon)
|
11
|
+
|
12
|
+
## Tools
|
13
|
+
|
14
|
+
This is automatically generated from OpenAPI schema for the Reddit API.
|
15
|
+
|
16
|
+
## Supported Integrations
|
17
|
+
|
18
|
+
This tool can be integrated with any service that supports HTTP requests.
|
19
|
+
|
20
|
+
## Tool List
|
21
|
+
|
22
|
+
| Tool | Description |
|
23
|
+
|------|-------------|
|
24
|
+
| create_post | Create a new post in a specified subreddit. |
|
25
|
+
| delete_content | Delete a Reddit post or comment. |
|
26
|
+
| edit_content | Edit the text content of a Reddit post or comment. |
|
27
|
+
| get_comment_by_id | Retrieve a specific Reddit comment by its full ID (t1_commentid). |
|
28
|
+
| get_post_flairs | Retrieve the list of available post flairs for a specific subreddit. |
|
29
|
+
| get_subreddit_posts | Get the top posts from a specified subreddit over a given timeframe. |
|
30
|
+
| post_comment | Post a comment to a Reddit post or another comment. |
|
31
|
+
| search_subreddits | Search for subreddits matching a query string. |
|
32
|
+
| validate | Function for validate |
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
## Usage
|
37
|
+
|
38
|
+
- Login to AgentR
|
39
|
+
- Follow the quickstart guide to setup MCP Server for your client
|
40
|
+
- Visit Apps Store and enable the Reddit app
|
41
|
+
- Restart the MCP Server
|
42
|
+
|
43
|
+
### Local Development
|
44
|
+
|
45
|
+
- Follow the README to test with the local MCP Server
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
# Resend MCP Server
|
3
|
+
|
4
|
+
An MCP Server for the Resend API.
|
5
|
+
|
6
|
+
## Supported Integrations
|
7
|
+
|
8
|
+
- AgentR
|
9
|
+
- API Key (Coming Soon)
|
10
|
+
- OAuth (Coming Soon)
|
11
|
+
|
12
|
+
## Tools
|
13
|
+
|
14
|
+
This is automatically generated from OpenAPI schema for the Resend API.
|
15
|
+
|
16
|
+
## Supported Integrations
|
17
|
+
|
18
|
+
This tool can be integrated with any service that supports HTTP requests.
|
19
|
+
|
20
|
+
## Tool List
|
21
|
+
|
22
|
+
| Tool | Description |
|
23
|
+
|------|-------------|
|
24
|
+
| send_email | Send an email using the Resend API |
|
25
|
+
| validate | Function for validate |
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
- Login to AgentR
|
32
|
+
- Follow the quickstart guide to setup MCP Server for your client
|
33
|
+
- Visit Apps Store and enable the Resend app
|
34
|
+
- Restart the MCP Server
|
35
|
+
|
36
|
+
### Local Development
|
37
|
+
|
38
|
+
- Follow the README to test with the local MCP Server
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
# Serp MCP Server
|
3
|
+
|
4
|
+
An MCP Server for the Serp API.
|
5
|
+
|
6
|
+
## Supported Integrations
|
7
|
+
|
8
|
+
- AgentR
|
9
|
+
- API Key (Coming Soon)
|
10
|
+
- OAuth (Coming Soon)
|
11
|
+
|
12
|
+
## Tools
|
13
|
+
|
14
|
+
This is automatically generated from OpenAPI schema for the Serp API.
|
15
|
+
|
16
|
+
## Supported Integrations
|
17
|
+
|
18
|
+
This tool can be integrated with any service that supports HTTP requests.
|
19
|
+
|
20
|
+
## Tool List
|
21
|
+
|
22
|
+
| Tool | Description |
|
23
|
+
|------|-------------|
|
24
|
+
| search | Perform a search on the specified engine using SerpApi. |
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
- Login to AgentR
|
31
|
+
- Follow the quickstart guide to setup MCP Server for your client
|
32
|
+
- Visit Apps Store and enable the Serp app
|
33
|
+
- Restart the MCP Server
|
34
|
+
|
35
|
+
### Local Development
|
36
|
+
|
37
|
+
- Follow the README to test with the local MCP Server
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
# Tavily MCP Server
|
3
|
+
|
4
|
+
An MCP Server for the Tavily API.
|
5
|
+
|
6
|
+
## Supported Integrations
|
7
|
+
|
8
|
+
- AgentR
|
9
|
+
- API Key (Coming Soon)
|
10
|
+
- OAuth (Coming Soon)
|
11
|
+
|
12
|
+
## Tools
|
13
|
+
|
14
|
+
This is automatically generated from OpenAPI schema for the Tavily API.
|
15
|
+
|
16
|
+
## Supported Integrations
|
17
|
+
|
18
|
+
This tool can be integrated with any service that supports HTTP requests.
|
19
|
+
|
20
|
+
## Tool List
|
21
|
+
|
22
|
+
| Tool | Description |
|
23
|
+
|------|-------------|
|
24
|
+
| search | Search the web using Tavily's search API |
|
25
|
+
| validate | Function for validate |
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
- Login to AgentR
|
32
|
+
- Follow the quickstart guide to setup MCP Server for your client
|
33
|
+
- Visit Apps Store and enable the Tavily app
|
34
|
+
- Restart the MCP Server
|
35
|
+
|
36
|
+
### Local Development
|
37
|
+
|
38
|
+
- Follow the README to test with the local MCP Server
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
# Zenquotes MCP Server
|
3
|
+
|
4
|
+
An MCP Server for the Zenquotes API.
|
5
|
+
|
6
|
+
## Supported Integrations
|
7
|
+
|
8
|
+
- AgentR
|
9
|
+
- API Key (Coming Soon)
|
10
|
+
- OAuth (Coming Soon)
|
11
|
+
|
12
|
+
## Tools
|
13
|
+
|
14
|
+
This is automatically generated from OpenAPI schema for the Zenquotes API.
|
15
|
+
|
16
|
+
## Supported Integrations
|
17
|
+
|
18
|
+
This tool can be integrated with any service that supports HTTP requests.
|
19
|
+
|
20
|
+
## Tool List
|
21
|
+
|
22
|
+
| Tool | Description |
|
23
|
+
|------|-------------|
|
24
|
+
| get_quote | Get an inspirational quote from the Zen Quotes API |
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
- Login to AgentR
|
31
|
+
- Follow the quickstart guide to setup MCP Server for your client
|
32
|
+
- Visit Apps Store and enable the Zenquotes app
|
33
|
+
- Restart the MCP Server
|
34
|
+
|
35
|
+
### Local Development
|
36
|
+
|
37
|
+
- Follow the README to test with the local MCP Server
|
universal_mcp/cli.py
CHANGED
@@ -110,16 +110,16 @@ def run(
|
|
110
110
|
):
|
111
111
|
"""Run the MCP server"""
|
112
112
|
from universal_mcp.config import ServerConfig
|
113
|
-
|
114
|
-
# from universal_mcp.logger import setup_logger
|
113
|
+
from universal_mcp.logger import setup_logger
|
115
114
|
from universal_mcp.servers import server_from_config
|
116
115
|
|
116
|
+
setup_logger()
|
117
|
+
|
117
118
|
if config_path:
|
118
119
|
config = ServerConfig.model_validate_json(config_path.read_text())
|
119
120
|
else:
|
120
121
|
config = ServerConfig()
|
121
122
|
server = server_from_config(config)
|
122
|
-
# setup_logger()
|
123
123
|
server.run(transport=config.transport)
|
124
124
|
|
125
125
|
|
universal_mcp/logger.py
CHANGED
@@ -53,16 +53,16 @@ def posthog_sink(message, user_id=get_user_id()):
|
|
53
53
|
|
54
54
|
def setup_logger():
|
55
55
|
logger.remove()
|
56
|
-
logger.add(
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
)
|
56
|
+
# logger.add(
|
57
|
+
# sink=sys.stdout,
|
58
|
+
# format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>",
|
59
|
+
# level="INFO",
|
60
|
+
# colorize=True,
|
61
|
+
# )
|
62
62
|
logger.add(
|
63
63
|
sink=sys.stderr,
|
64
64
|
format="<red>{time:YYYY-MM-DD HH:mm:ss}</red> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>",
|
65
|
-
level="
|
65
|
+
level="WARNING",
|
66
66
|
colorize=True,
|
67
67
|
)
|
68
68
|
telemetry_enabled = os.getenv("TELEMETRY_ENABLED", "true").lower() == "true"
|
@@ -0,0 +1,68 @@
|
|
1
|
+
import csv
|
2
|
+
from pathlib import Path
|
3
|
+
|
4
|
+
from universal_mcp.applications import app_from_slug
|
5
|
+
|
6
|
+
|
7
|
+
def discover_available_app_slugs():
|
8
|
+
apps_dir = Path(__file__).resolve().parent.parent / "applications"
|
9
|
+
app_slugs = []
|
10
|
+
|
11
|
+
for item in apps_dir.iterdir():
|
12
|
+
if not item.is_dir() or item.name.startswith('_'):
|
13
|
+
continue
|
14
|
+
|
15
|
+
if (item / "app.py").exists():
|
16
|
+
slug = item.name.replace("_", "-")
|
17
|
+
app_slugs.append(slug)
|
18
|
+
|
19
|
+
return app_slugs
|
20
|
+
|
21
|
+
def extract_app_tools(app_slugs):
|
22
|
+
all_apps_tools = []
|
23
|
+
|
24
|
+
for slug in app_slugs:
|
25
|
+
try:
|
26
|
+
print(f"Loading app: {slug}")
|
27
|
+
app_class = app_from_slug(slug)
|
28
|
+
|
29
|
+
app_instance = app_class(integration=None)
|
30
|
+
|
31
|
+
tools = app_instance.list_tools()
|
32
|
+
|
33
|
+
for tool in tools:
|
34
|
+
tool_name = tool.__name__
|
35
|
+
description = tool.__doc__.strip().split('\n')[0] if tool.__doc__ else "No description"
|
36
|
+
|
37
|
+
all_apps_tools.append({
|
38
|
+
"app_name": slug,
|
39
|
+
"tool_name": tool_name,
|
40
|
+
"description": description
|
41
|
+
})
|
42
|
+
|
43
|
+
except Exception as e:
|
44
|
+
print(f"Error loading app {slug}: {e}")
|
45
|
+
|
46
|
+
return all_apps_tools
|
47
|
+
|
48
|
+
def write_to_csv(app_tools, output_file="app_tools.csv"):
|
49
|
+
fieldnames = ["app_name", "tool_name", "description"]
|
50
|
+
|
51
|
+
with open(output_file, 'w', newline='') as csvfile:
|
52
|
+
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
|
53
|
+
writer.writeheader()
|
54
|
+
writer.writerows(app_tools)
|
55
|
+
|
56
|
+
print(f"CSV file created: {output_file}")
|
57
|
+
|
58
|
+
def main():
|
59
|
+
app_slugs = discover_available_app_slugs()
|
60
|
+
print(f"Found {len(app_slugs)} app slugs: {', '.join(app_slugs)}")
|
61
|
+
|
62
|
+
app_tools = extract_app_tools(app_slugs)
|
63
|
+
print(f"Extracted {len(app_tools)} tools from all apps")
|
64
|
+
|
65
|
+
write_to_csv(app_tools)
|
66
|
+
|
67
|
+
if __name__ == "__main__":
|
68
|
+
main()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: universal-mcp
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.6
|
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
|
Requires-Python: >=3.11
|
@@ -1,26 +1,41 @@
|
|
1
1
|
universal_mcp/__init__.py,sha256=2gdHpHaDDcsRjZjJ01FLN-1iidN_wbDAolNpxhGoFB4,59
|
2
|
-
universal_mcp/cli.py,sha256=
|
2
|
+
universal_mcp/cli.py,sha256=DG-Qxc5vQIdbhAIQuU7bKKJuRGzwyOigjfCKSWBRhBI,5258
|
3
3
|
universal_mcp/config.py,sha256=9eb3DDg4PBBr1MlGeBrA4bja3Y6howOH-UKpo7JIbs8,828
|
4
4
|
universal_mcp/exceptions.py,sha256=Zp2_v_m3L7GDAmD1ZyuwFtY6ngapdhxuIygrvpZAQtM,271
|
5
|
-
universal_mcp/logger.py,sha256=
|
5
|
+
universal_mcp/logger.py,sha256=W6A868vyvpdkEQ4Dd0rWdC_7ErSxSQ1z2uxCb77IM8Y,2015
|
6
6
|
universal_mcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
7
|
universal_mcp/applications/__init__.py,sha256=qeWnbdIudyMR7ST4XTc0gpEM9o6TsM1ZnZ92dMAPSBA,754
|
8
8
|
universal_mcp/applications/application.py,sha256=dqp8lgIi2xhY62imwo7C6769URQtNmqd6Ok6PiTr6wc,3399
|
9
|
+
universal_mcp/applications/e2b/README.md,sha256=S4lTp-vEZ8VTCKPXqjUXu5nYlUMAF8lw8CQyBGPgxjs,700
|
9
10
|
universal_mcp/applications/e2b/app.py,sha256=5piCipi1TC_KuKLFP17do1Y1r28fqApvRsZy76equ9w,2573
|
11
|
+
universal_mcp/applications/firecrawl/README.md,sha256=KAWe_TQbrc9eA6bSyde5dapMP1CNvarVItV_YJH3d_0,1430
|
10
12
|
universal_mcp/applications/firecrawl/app.py,sha256=RSy8zRn4k1A1tIpJNqrUnPI8ctEv1nKWuOuJQcp9mGo,9264
|
11
|
-
universal_mcp/applications/github/README.md,sha256=
|
13
|
+
universal_mcp/applications/github/README.md,sha256=6ID-__gUJ5ZxzAS_OjzmoUAag1LamSvEB75DHcj3m-g,1294
|
12
14
|
universal_mcp/applications/github/app.py,sha256=L201f5MSx1YVx0nqgduZ5gyHPZdX0UfcEhPmDWiWK6s,13686
|
13
15
|
universal_mcp/applications/google_calendar/app.py,sha256=g_3vrsM2ltwpTySgC5I4SYg47n4UJiYigECO0ax1EHM,19134
|
16
|
+
universal_mcp/applications/google_docs/README.md,sha256=SyOgJG-XU0FXhrVukvg9mxwin73mpqaCOT6rDJ64Klk,909
|
17
|
+
universal_mcp/applications/google_docs/app.py,sha256=RGpk4xW6u2rA62zkjsokDbhff3bk_bGt261ToQlAQNY,3157
|
18
|
+
universal_mcp/applications/google_drive/README.md,sha256=YlN8IT12oO8zVMib1MlTYRBGNP7rzW_KyVAZyyxKvME,1192
|
19
|
+
universal_mcp/applications/google_drive/app.py,sha256=PUK19kZlYNWn3_4Us1179_gc6o_565B4hsfxUleeR68,9546
|
20
|
+
universal_mcp/applications/google_mail/README.md,sha256=LL6TjjmwEqyuRVvIfCh-I_PlWp9ciCZOdJC0LafGZYc,1185
|
14
21
|
universal_mcp/applications/google_mail/app.py,sha256=VXeD3_TRgYIUDFUzDPCKgR47XvovxLFulD-HG22hls8,22716
|
22
|
+
universal_mcp/applications/google_sheet/README.md,sha256=yW1b_qlb_pbIJzCxZc58581kKzC5vyP8Mj4iwXgidtQ,1108
|
23
|
+
universal_mcp/applications/google_sheet/app.py,sha256=zYsi_vTpJ-BtBPH6rQFRfwAJJkN_d8DJm6tFUzJBOSw,5399
|
15
24
|
universal_mcp/applications/markitdown/app.py,sha256=Gh12f1dW6DA_AW5DuStbCkOR7KtyJp8VEjdTaIicrRI,1996
|
16
25
|
universal_mcp/applications/notion/README.md,sha256=45NmPOmSQv99qBvWdwmnV5vbaYc9_8vq8I-FA7veVAA,2600
|
17
26
|
universal_mcp/applications/notion/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
18
27
|
universal_mcp/applications/notion/app.py,sha256=XpLnmeXj0Gnf_RYHlbAFnwtSCTYsrNzk6MSMSyDmHGQ,17283
|
28
|
+
universal_mcp/applications/perplexity/README.md,sha256=QGV1iReH5p-Np7vvkZsVHxxDKQ0YaitHEwomNmGEyQs,732
|
19
29
|
universal_mcp/applications/perplexity/app.py,sha256=J27IDcz9pRC_uW4wABpn-EcI4RvKIzoh_o2hzysM5wA,3187
|
30
|
+
universal_mcp/applications/reddit/README.md,sha256=YVbJ1RN6NWlB-P6w2LxCk_DuUWl7mwaKZScY-mIMnNc,1271
|
20
31
|
universal_mcp/applications/reddit/app.py,sha256=leU__w5VxX1vMK-kfuy-dvY97Pn8Mn80X2payVshirU,13562
|
32
|
+
universal_mcp/applications/resend/README.md,sha256=k-sb2UwbFvDPEz6qQPLWd2cJj8hDx5f3NW7dz2jAfjI,719
|
21
33
|
universal_mcp/applications/resend/app.py,sha256=bRo-CRDuk65EUSHOJnbVHWV6TuiUHtedz6FXKRS1ym0,1386
|
34
|
+
universal_mcp/applications/serp/README.md,sha256=hX4VeT2iL_67ZsMhKd60DAujQCh9K3IdHroHIq808RY,691
|
22
35
|
universal_mcp/applications/serp/app.py,sha256=hPXu1sBiRZRCCzr4q2uvt54F0-B3aZK2Uz4wfKokkZ4,3131
|
36
|
+
universal_mcp/applications/tavily/README.md,sha256=cNg4EwX5wBbkDpPtNBNC3A_GxglfSVhdAJuweSrXN20,721
|
23
37
|
universal_mcp/applications/tavily/app.py,sha256=FSIRFz7jwm2i9wiDpRWIoQBxHaJ6dl89AjSZY9WP2VE,1776
|
38
|
+
universal_mcp/applications/zenquotes/README.md,sha256=wA3hjqjrkrczQaffpwyolSKq6gXmkLgeHx6_EQrYEOY,709
|
24
39
|
universal_mcp/applications/zenquotes/app.py,sha256=nidRGwVORIU25QGCCbjDIv1UNFUj5nWA3qqK4JP0Tdg,621
|
25
40
|
universal_mcp/integrations/README.md,sha256=lTAPXO2nivcBe1q7JT6PRa6v9Ns_ZersQMIdw-nmwEA,996
|
26
41
|
universal_mcp/integrations/__init__.py,sha256=8e11JZyctaR9CmlNkfEZ6HhGDvhlvf9iug2wdjb5pwY,270
|
@@ -34,9 +49,10 @@ universal_mcp/utils/__init__.py,sha256=8wi4PGWu-SrFjNJ8U7fr2iFJ1ktqlDmSKj1xYd7KS
|
|
34
49
|
universal_mcp/utils/api_generator.py,sha256=-wRBpLVfJQXy1R-8FpDNs6b8_eeekVDuPc_uwjSGgiY,8883
|
35
50
|
universal_mcp/utils/bridge.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
36
51
|
universal_mcp/utils/docgen.py,sha256=yK6Ijo8G-wHPU3E1AnFpnXS9vXt2j9FM77w0etTaNOA,12639
|
52
|
+
universal_mcp/utils/dump_app_tools.py,sha256=cLB9SumKsbs-rXJ_02lpMyyNkOmKZ57gekhCjhAlcHg,2009
|
37
53
|
universal_mcp/utils/installation.py,sha256=uSL_H76fG_7yN4QNxkfp1mEF_00iAPyiXqtdWEMVJe8,3747
|
38
54
|
universal_mcp/utils/openapi.py,sha256=ud_ZB7_60BcS1Vao7ESKDqo0gry9JN5wzy-CFssrjm8,13140
|
39
|
-
universal_mcp-0.1.
|
40
|
-
universal_mcp-0.1.
|
41
|
-
universal_mcp-0.1.
|
42
|
-
universal_mcp-0.1.
|
55
|
+
universal_mcp-0.1.6.dist-info/METADATA,sha256=8R7LIzbEuCr2cB6yaLxZG8HD9ISZz_H0jJqquPXSiaw,10852
|
56
|
+
universal_mcp-0.1.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
57
|
+
universal_mcp-0.1.6.dist-info/entry_points.txt,sha256=QlBrVKmA2jIM0q-C-3TQMNJTTWOsOFQvgedBq2rZTS8,56
|
58
|
+
universal_mcp-0.1.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|