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.
@@ -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
- No tools with documentation were found in this API client.
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
- 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
- )
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="ERROR",
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.5
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=SO-d2a4QI6e3TLjDcwP6pGF0oWzummOh3a2CJBbqA68,5262
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=8_yoD3mGbm0VpJitb0k_DJskC2kcRNi2dqpKTdz7ijo,2001
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=m_6FlPpx9l5y29TkFMewCXMXSRHriDtj71qyYYeFzCw,643
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.5.dist-info/METADATA,sha256=ZnW_mze17x2bfioT-ZaurQcfxpA3j5tbNuIfvO_sCwE,10852
40
- universal_mcp-0.1.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
41
- universal_mcp-0.1.5.dist-info/entry_points.txt,sha256=QlBrVKmA2jIM0q-C-3TQMNJTTWOsOFQvgedBq2rZTS8,56
42
- universal_mcp-0.1.5.dist-info/RECORD,,
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,,