universal-mcp-applications 0.1.21__py3-none-any.whl → 0.1.23__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.
Potentially problematic release.
This version of universal-mcp-applications might be problematic. Click here for more details.
- universal_mcp/applications/BEST_PRACTICES.md +166 -0
- universal_mcp/applications/airtable/app.py +0 -1
- universal_mcp/applications/apollo/app.py +0 -1
- universal_mcp/applications/aws_s3/app.py +40 -39
- universal_mcp/applications/browser_use/README.md +1 -0
- universal_mcp/applications/browser_use/__init__.py +0 -0
- universal_mcp/applications/browser_use/app.py +71 -0
- universal_mcp/applications/calendly/app.py +125 -125
- universal_mcp/applications/canva/app.py +95 -99
- universal_mcp/applications/confluence/app.py +0 -1
- universal_mcp/applications/contentful/app.py +4 -5
- universal_mcp/applications/domain_checker/app.py +11 -15
- universal_mcp/applications/e2b/app.py +4 -4
- universal_mcp/applications/elevenlabs/app.py +18 -15
- universal_mcp/applications/exa/app.py +17 -17
- universal_mcp/applications/falai/app.py +28 -29
- universal_mcp/applications/file_system/app.py +9 -9
- universal_mcp/applications/firecrawl/app.py +36 -36
- universal_mcp/applications/fireflies/app.py +55 -56
- universal_mcp/applications/fpl/app.py +49 -50
- universal_mcp/applications/ghost_content/app.py +0 -1
- universal_mcp/applications/github/app.py +41 -43
- universal_mcp/applications/google_calendar/app.py +40 -39
- universal_mcp/applications/google_docs/app.py +168 -232
- universal_mcp/applications/google_drive/app.py +212 -215
- universal_mcp/applications/google_gemini/app.py +1 -5
- universal_mcp/applications/google_mail/app.py +91 -90
- universal_mcp/applications/google_searchconsole/app.py +29 -29
- universal_mcp/applications/google_sheet/app.py +115 -115
- universal_mcp/applications/hashnode/README.md +6 -3
- universal_mcp/applications/hashnode/app.py +174 -25
- universal_mcp/applications/http_tools/app.py +10 -11
- universal_mcp/applications/hubspot/__init__.py +1 -1
- universal_mcp/applications/hubspot/api_segments/api_segment_base.py +36 -7
- universal_mcp/applications/hubspot/api_segments/crm_api.py +368 -368
- universal_mcp/applications/hubspot/api_segments/marketing_api.py +115 -115
- universal_mcp/applications/hubspot/app.py +131 -72
- universal_mcp/applications/jira/app.py +0 -1
- universal_mcp/applications/linkedin/app.py +20 -20
- universal_mcp/applications/markitdown/app.py +10 -5
- universal_mcp/applications/ms_teams/app.py +123 -123
- universal_mcp/applications/openai/app.py +40 -39
- universal_mcp/applications/outlook/app.py +32 -32
- universal_mcp/applications/perplexity/app.py +4 -4
- universal_mcp/applications/reddit/app.py +69 -70
- universal_mcp/applications/resend/app.py +116 -117
- universal_mcp/applications/rocketlane/app.py +0 -1
- universal_mcp/applications/scraper/__init__.py +1 -1
- universal_mcp/applications/scraper/app.py +80 -81
- universal_mcp/applications/serpapi/app.py +14 -14
- universal_mcp/applications/sharepoint/app.py +19 -20
- universal_mcp/applications/shopify/app.py +0 -1
- universal_mcp/applications/slack/app.py +48 -48
- universal_mcp/applications/tavily/app.py +4 -4
- universal_mcp/applications/twitter/api_segments/compliance_api.py +13 -15
- universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +20 -20
- universal_mcp/applications/twitter/api_segments/dm_events_api.py +12 -12
- universal_mcp/applications/twitter/api_segments/likes_api.py +12 -12
- universal_mcp/applications/twitter/api_segments/lists_api.py +37 -39
- universal_mcp/applications/twitter/api_segments/spaces_api.py +24 -24
- universal_mcp/applications/twitter/api_segments/trends_api.py +4 -4
- universal_mcp/applications/twitter/api_segments/tweets_api.py +105 -105
- universal_mcp/applications/twitter/api_segments/usage_api.py +4 -4
- universal_mcp/applications/twitter/api_segments/users_api.py +136 -136
- universal_mcp/applications/twitter/app.py +6 -2
- universal_mcp/applications/unipile/app.py +90 -97
- universal_mcp/applications/whatsapp/app.py +53 -54
- universal_mcp/applications/whatsapp/audio.py +39 -35
- universal_mcp/applications/whatsapp/whatsapp.py +176 -154
- universal_mcp/applications/whatsapp_business/app.py +92 -92
- universal_mcp/applications/yahoo_finance/app.py +105 -63
- universal_mcp/applications/youtube/app.py +193 -196
- universal_mcp/applications/zenquotes/__init__.py +2 -0
- universal_mcp/applications/zenquotes/app.py +5 -5
- {universal_mcp_applications-0.1.21.dist-info → universal_mcp_applications-0.1.23.dist-info}/METADATA +2 -1
- {universal_mcp_applications-0.1.21.dist-info → universal_mcp_applications-0.1.23.dist-info}/RECORD +78 -74
- {universal_mcp_applications-0.1.21.dist-info → universal_mcp_applications-0.1.23.dist-info}/WHEEL +0 -0
- {universal_mcp_applications-0.1.21.dist-info → universal_mcp_applications-0.1.23.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Best Practices for Creating MCP Applications
|
|
2
|
+
|
|
3
|
+
This guide provides best practices for developing new applications for the Universal MCP. We will use the `google_gemini` application as a reference to illustrate key concepts.
|
|
4
|
+
|
|
5
|
+
## 1. Application Structure
|
|
6
|
+
|
|
7
|
+
Every application should be a class that inherits from `APIApplication`. This base class provides the necessary foundation for integration with the MCP.
|
|
8
|
+
|
|
9
|
+
### Basic Class Definition
|
|
10
|
+
|
|
11
|
+
Your `app.py` file should contain a class definition similar to this:
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from universal_mcp.applications.application import APIApplication
|
|
15
|
+
from universal_mcp.integrations import Integration
|
|
16
|
+
|
|
17
|
+
class YourApplicationName(APIApplication):
|
|
18
|
+
def __init__(self, integration: Integration = None, **kwargs) -> None:
|
|
19
|
+
super().__init__(name="your_application_name", integration=integration, **kwargs)
|
|
20
|
+
self._api_client = None # Lazy-loaded client
|
|
21
|
+
|
|
22
|
+
# ... your tool methods will go here
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- **`__init__`**: The constructor initializes the base application with a unique `name` (usually matching the folder name) and the `integration` object, which holds credentials.
|
|
26
|
+
- **`_api_client`**: It's a best practice to lazy-load your API client. Initialize it as `None` and create the client instance only when it's first needed.
|
|
27
|
+
|
|
28
|
+
## 2. Authentication and API Clients
|
|
29
|
+
|
|
30
|
+
Credentials should never be hard-coded. They are managed through the `integration` object. Use a property to initialize your API client on its first use. This prevents unnecessary setup if the application is loaded but not used.
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from google import genai
|
|
34
|
+
|
|
35
|
+
class GoogleGeminiApp(APIApplication):
|
|
36
|
+
# ... (init method)
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def genai_client(self) -> genai.Client:
|
|
40
|
+
if self._genai_client is not None:
|
|
41
|
+
return self._genai_client
|
|
42
|
+
|
|
43
|
+
credentials = self.integration.get_credentials()
|
|
44
|
+
api_key = (
|
|
45
|
+
credentials.get("api_key")
|
|
46
|
+
or credentials.get("API_KEY")
|
|
47
|
+
or credentials.get("apiKey")
|
|
48
|
+
)
|
|
49
|
+
if not api_key:
|
|
50
|
+
raise ValueError("API key not found in integration credentials")
|
|
51
|
+
|
|
52
|
+
self._genai_client = genai.Client(api_key=api_key)
|
|
53
|
+
return self._genai_client
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
This `genai_client` property ensures the client is created only once, the first time a tool tries to access it.
|
|
57
|
+
|
|
58
|
+
## 3. Defining Tools
|
|
59
|
+
|
|
60
|
+
Public methods within your application class are exposed as tools that the AI can use. For a method to be a valid tool, it must have a detailed docstring and proper type hints.
|
|
61
|
+
|
|
62
|
+
### Docstrings: The AI's Guide
|
|
63
|
+
|
|
64
|
+
A well-written docstring is crucial. It's how the AI understands what your tool does, what inputs it needs, and what it returns.
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
async def generate_text(
|
|
68
|
+
self,
|
|
69
|
+
prompt: Annotated[str, "The prompt to generate text from"],
|
|
70
|
+
model: str = "gemini-2.5-flash",
|
|
71
|
+
) -> str:
|
|
72
|
+
"""Generates text using the Google Gemini model based on a given prompt.
|
|
73
|
+
This tool is suitable for various natural language processing tasks such as content generation, summarization, translation, and question answering.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
prompt (str): The text prompt or instruction for the model to follow. For example: "Write a short story about a robot who learns to love."
|
|
77
|
+
model (str, optional): The Gemini model to use for text generation. Defaults to "gemini-2.5-flash".
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
str: The generated text response from the Gemini model.
|
|
81
|
+
|
|
82
|
+
Raises:
|
|
83
|
+
ValueError: If the API key is not found in the integration credentials.
|
|
84
|
+
Exception: If the underlying client or API call fails.
|
|
85
|
+
|
|
86
|
+
Tags:
|
|
87
|
+
text, generate, llm, important
|
|
88
|
+
"""
|
|
89
|
+
response = self.genai_client.models.generate_content(
|
|
90
|
+
contents=prompt, model=model
|
|
91
|
+
)
|
|
92
|
+
return response.text
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Docstring Breakdown:**
|
|
96
|
+
1. **Summary Line**: A single, concise sentence describing the tool's main function.
|
|
97
|
+
2. **Detailed Description**: An optional paragraph providing more context, use cases, and why the tool is useful.
|
|
98
|
+
3. **`Args`**: Describe each parameter. For clarity, use `typing.Annotated` to provide a short, user-friendly description directly in the function signature.
|
|
99
|
+
4. **`Returns`**: Clearly describe the output of the function.
|
|
100
|
+
5. **`Raises`**: List any potential exceptions the tool might raise.
|
|
101
|
+
6. **`Tags`**: A comma-separated list of keywords that help in categorizing and discovering the tool. Use `important` for core tools.
|
|
102
|
+
|
|
103
|
+
### Return Types
|
|
104
|
+
|
|
105
|
+
- **Simple Types**: For simple text or data, return standard Python types like `str`, `dict`, or `list`.
|
|
106
|
+
|
|
107
|
+
- **Files/Media (Images, Audio, etc.)**: When a tool generates content that should be treated as a file, return a specific dictionary structure. This allows the MCP to handle the data correctly (e.g., saving it to a file or displaying it).
|
|
108
|
+
|
|
109
|
+
The structure is:
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"type": "image" | "audio" | "video" | "file",
|
|
113
|
+
"data": "<base64_encoded_string>",
|
|
114
|
+
"mime_type": "image/png",
|
|
115
|
+
"file_name": "suggested_filename.png"
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Example from `generate_image`:**
|
|
120
|
+
```python
|
|
121
|
+
return {
|
|
122
|
+
"type": "image",
|
|
123
|
+
"data": img_base64,
|
|
124
|
+
"mime_type": "image/png",
|
|
125
|
+
"file_name": file_name,
|
|
126
|
+
"text": text, # Optional: any accompanying text
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## 4. Listing and Registering Tools
|
|
131
|
+
|
|
132
|
+
To make your tools available to the MCP, you must list them in the `list_tools` method.
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
class GoogleGeminiApp(APIApplication):
|
|
136
|
+
# ... (tool methods)
|
|
137
|
+
|
|
138
|
+
def list_tools(self):
|
|
139
|
+
return [
|
|
140
|
+
self.generate_text,
|
|
141
|
+
self.generate_image,
|
|
142
|
+
self.generate_audio,
|
|
143
|
+
]
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
This method should return a list of the function objects you want to expose.
|
|
147
|
+
|
|
148
|
+
## 5. Local Testing
|
|
149
|
+
|
|
150
|
+
To facilitate rapid development and testing, include a runnable block in your `app.py`. This allows you to test your application's functionality directly without needing the full MCP environment.
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
if __name__ == "__main__":
|
|
154
|
+
import asyncio
|
|
155
|
+
|
|
156
|
+
async def test_google_gemini():
|
|
157
|
+
# NOTE: You may need to set up credentials locally for this to work
|
|
158
|
+
# e.g., os.environ["GOOGLE_API_KEY"] = "..."
|
|
159
|
+
app = GoogleGeminiApp()
|
|
160
|
+
response = await app.generate_text(
|
|
161
|
+
"Tell me a fun fact about the Roman Empire."
|
|
162
|
+
)
|
|
163
|
+
print(response)
|
|
164
|
+
|
|
165
|
+
asyncio.run(test_google_gemini())
|
|
166
|
+
```
|
|
@@ -4,7 +4,6 @@ from typing import Any
|
|
|
4
4
|
|
|
5
5
|
import boto3
|
|
6
6
|
from botocore.exceptions import ClientError
|
|
7
|
-
|
|
8
7
|
from universal_mcp.applications.application import BaseApplication
|
|
9
8
|
from universal_mcp.integrations import Integration
|
|
10
9
|
|
|
@@ -49,7 +48,7 @@ class AwsS3App(BaseApplication):
|
|
|
49
48
|
def list_buckets(self) -> list[str]:
|
|
50
49
|
"""
|
|
51
50
|
Retrieves all S3 buckets accessible by the configured AWS credentials. It calls the S3 API's list_buckets operation and processes the response to return a simple list containing just the names of the buckets.
|
|
52
|
-
|
|
51
|
+
|
|
53
52
|
Returns:
|
|
54
53
|
List[str]: A list of bucket names.
|
|
55
54
|
"""
|
|
@@ -59,11 +58,11 @@ class AwsS3App(BaseApplication):
|
|
|
59
58
|
def create_bucket(self, bucket_name: str, region: str | None = None) -> bool:
|
|
60
59
|
"""
|
|
61
60
|
Creates a new Amazon S3 bucket with a specified name and optional region. Returns `True` upon successful creation, or `False` if an AWS client error, such as a naming conflict or permission issue, occurs.
|
|
62
|
-
|
|
61
|
+
|
|
63
62
|
Args:
|
|
64
63
|
bucket_name (str): The name of the bucket to create.
|
|
65
64
|
region (str, optional): The region to create the bucket in.
|
|
66
|
-
|
|
65
|
+
|
|
67
66
|
Returns:
|
|
68
67
|
bool: True if the bucket was created successfully.
|
|
69
68
|
Tags:
|
|
@@ -84,10 +83,10 @@ class AwsS3App(BaseApplication):
|
|
|
84
83
|
def delete_bucket(self, bucket_name: str) -> bool:
|
|
85
84
|
"""
|
|
86
85
|
Deletes a specified S3 bucket. The operation requires the bucket to be empty to succeed. It returns `True` if the bucket is successfully deleted and `False` if an error occurs, such as the bucket not being found or containing objects.
|
|
87
|
-
|
|
86
|
+
|
|
88
87
|
Args:
|
|
89
88
|
bucket_name (str): The name of the bucket to delete.
|
|
90
|
-
|
|
89
|
+
|
|
91
90
|
Returns:
|
|
92
91
|
bool: True if the bucket was deleted successfully.
|
|
93
92
|
Tags:
|
|
@@ -102,10 +101,10 @@ class AwsS3App(BaseApplication):
|
|
|
102
101
|
def get_bucket_policy(self, bucket_name: str) -> dict[str, Any]:
|
|
103
102
|
"""
|
|
104
103
|
Retrieves the IAM resource policy for a specified S3 bucket, parsing the JSON string into a dictionary. If the operation fails due to permissions or a non-existent policy, it returns an error dictionary. This complements `put_bucket_policy`, which applies a new policy.
|
|
105
|
-
|
|
104
|
+
|
|
106
105
|
Args:
|
|
107
106
|
bucket_name (str): The name of the S3 bucket.
|
|
108
|
-
|
|
107
|
+
|
|
109
108
|
Returns:
|
|
110
109
|
Dict[str, Any]: The bucket policy as a dictionary.
|
|
111
110
|
Tags:
|
|
@@ -120,11 +119,11 @@ class AwsS3App(BaseApplication):
|
|
|
120
119
|
def set_bucket_policy(self, bucket_name: str, policy: dict[str, Any]) -> bool:
|
|
121
120
|
"""
|
|
122
121
|
Applies or replaces the access policy for a specified S3 bucket. The function accepts the policy as a dictionary, converts it to JSON, and assigns it to the bucket. This write operation is the counterpart to `get_bucket_policy` and returns `True` on success.
|
|
123
|
-
|
|
122
|
+
|
|
124
123
|
Args:
|
|
125
124
|
bucket_name (str): The name of the S3 bucket.
|
|
126
125
|
policy (Dict[str, Any]): The bucket policy as a dictionary.
|
|
127
|
-
|
|
126
|
+
|
|
128
127
|
Returns:
|
|
129
128
|
bool: True if the policy was set successfully.
|
|
130
129
|
Tags:
|
|
@@ -136,14 +135,16 @@ class AwsS3App(BaseApplication):
|
|
|
136
135
|
except ClientError:
|
|
137
136
|
return False
|
|
138
137
|
|
|
139
|
-
def list_subdirectories(
|
|
138
|
+
def list_subdirectories(
|
|
139
|
+
self, bucket_name: str, prefix: str | None = None
|
|
140
|
+
) -> list[str]:
|
|
140
141
|
"""
|
|
141
142
|
Lists immediate subdirectories (common prefixes) within an S3 bucket. If a prefix is provided, it returns subdirectories under that path; otherwise, it lists top-level directories. This function specifically lists folders, distinguishing it from `list_objects`, which lists files.
|
|
142
|
-
|
|
143
|
+
|
|
143
144
|
Args:
|
|
144
145
|
bucket_name (str): The name of the S3 bucket.
|
|
145
146
|
prefix (str, optional): The prefix to list folders under.
|
|
146
|
-
|
|
147
|
+
|
|
147
148
|
Returns:
|
|
148
149
|
List[str]: A list of folder prefixes.
|
|
149
150
|
Tags:
|
|
@@ -168,12 +169,12 @@ class AwsS3App(BaseApplication):
|
|
|
168
169
|
) -> bool:
|
|
169
170
|
"""
|
|
170
171
|
Creates a prefix (folder) in an S3 bucket, optionally nested under a parent prefix. It simulates a directory by creating a zero-byte object with a key ending in a slash ('/'), distinguishing it from put_object, which uploads content.
|
|
171
|
-
|
|
172
|
+
|
|
172
173
|
Args:
|
|
173
174
|
bucket_name (str): The name of the S3 bucket.
|
|
174
175
|
prefix_name (str): The name of the prefix to create.
|
|
175
176
|
parent_prefix (str, optional): The parent prefix (folder path).
|
|
176
|
-
|
|
177
|
+
|
|
177
178
|
Returns:
|
|
178
179
|
bool: True if the prefix was created successfully.
|
|
179
180
|
Tags:
|
|
@@ -189,11 +190,11 @@ class AwsS3App(BaseApplication):
|
|
|
189
190
|
def list_objects(self, bucket_name: str, prefix: str) -> list[dict[str, Any]]:
|
|
190
191
|
"""
|
|
191
192
|
Paginates through and lists all objects within a specified S3 bucket prefix. It returns a curated list of metadata for each object (key, name, size, last modified), excluding folder placeholders. This function specifically lists files, distinguishing it from `list_prefixes` which lists folders.
|
|
192
|
-
|
|
193
|
+
|
|
193
194
|
Args:
|
|
194
195
|
bucket_name (str): The name of the S3 bucket.
|
|
195
196
|
prefix (str): The prefix (folder path) to list objects under.
|
|
196
|
-
|
|
197
|
+
|
|
197
198
|
Returns:
|
|
198
199
|
List[Dict[str, Any]]: A list of dictionaries containing object metadata.
|
|
199
200
|
Tags:
|
|
@@ -222,13 +223,13 @@ class AwsS3App(BaseApplication):
|
|
|
222
223
|
) -> bool:
|
|
223
224
|
"""
|
|
224
225
|
Uploads string content to create an object in a specified S3 bucket and prefix. The content is UTF-8 encoded before being written. This method is for text, distinguishing it from `put_object_from_base64` which handles binary data.
|
|
225
|
-
|
|
226
|
+
|
|
226
227
|
Args:
|
|
227
228
|
bucket_name (str): The name of the S3 bucket.
|
|
228
229
|
prefix (str): The prefix (folder path) where the object will be created.
|
|
229
230
|
object_name (str): The name of the object to create.
|
|
230
231
|
content (str): The content to write into the object.
|
|
231
|
-
|
|
232
|
+
|
|
232
233
|
Returns:
|
|
233
234
|
bool: True if the object was created successfully.
|
|
234
235
|
Tags:
|
|
@@ -245,13 +246,13 @@ class AwsS3App(BaseApplication):
|
|
|
245
246
|
) -> bool:
|
|
246
247
|
"""
|
|
247
248
|
Decodes a base64 string into binary data and uploads it as an object to a specified S3 location. This method is designed for binary files, differentiating it from `put_object` which handles plain text content. Returns true on success.
|
|
248
|
-
|
|
249
|
+
|
|
249
250
|
Args:
|
|
250
251
|
bucket_name (str): The name of the S3 bucket.
|
|
251
252
|
prefix (str): The prefix (folder path) where the object will be created.
|
|
252
253
|
object_name (str): The name of the object to create.
|
|
253
254
|
base64_content (str): The base64-encoded content to upload.
|
|
254
|
-
|
|
255
|
+
|
|
255
256
|
Returns:
|
|
256
257
|
bool: True if the object was created successfully.
|
|
257
258
|
Tags:
|
|
@@ -268,11 +269,11 @@ class AwsS3App(BaseApplication):
|
|
|
268
269
|
def get_object_with_content(self, bucket_name: str, key: str) -> dict[str, Any]:
|
|
269
270
|
"""
|
|
270
271
|
Retrieves an S3 object's content. It decodes text files as UTF-8 or encodes binary files as base64 based on the object's key. This function downloads the full object body, unlike `get_object_metadata` which only fetches metadata without content, returning the content, name, size, and type.
|
|
271
|
-
|
|
272
|
+
|
|
272
273
|
Args:
|
|
273
274
|
bucket_name (str): The name of the S3 bucket.
|
|
274
275
|
key (str): The key (path) to the object.
|
|
275
|
-
|
|
276
|
+
|
|
276
277
|
Returns:
|
|
277
278
|
Dict[str, Any]: A dictionary containing the object's name, content type, content (as text or base64), and size.
|
|
278
279
|
Tags:
|
|
@@ -301,11 +302,11 @@ class AwsS3App(BaseApplication):
|
|
|
301
302
|
def get_object_metadata(self, bucket_name: str, key: str) -> dict[str, Any]:
|
|
302
303
|
"""
|
|
303
304
|
Efficiently retrieves metadata for a specified S3 object, such as size and last modified date, without downloading its content. This function uses a HEAD request, making it faster than `get_object_content` for accessing object properties. Returns a dictionary of metadata or an error message on failure.
|
|
304
|
-
|
|
305
|
+
|
|
305
306
|
Args:
|
|
306
307
|
bucket_name (str): The name of the S3 bucket.
|
|
307
308
|
key (str): The key (path) to the object.
|
|
308
|
-
|
|
309
|
+
|
|
309
310
|
Returns:
|
|
310
311
|
Dict[str, Any]: A dictionary containing the object's metadata.
|
|
311
312
|
Tags:
|
|
@@ -332,13 +333,13 @@ class AwsS3App(BaseApplication):
|
|
|
332
333
|
) -> bool:
|
|
333
334
|
"""
|
|
334
335
|
Copies an S3 object from a specified source location to a destination, which can be in the same or a different bucket. Unlike `move_object`, the original object remains at the source after the copy operation. It returns `True` for a successful operation.
|
|
335
|
-
|
|
336
|
+
|
|
336
337
|
Args:
|
|
337
338
|
source_bucket (str): The source bucket name.
|
|
338
339
|
source_key (str): The source object key.
|
|
339
340
|
dest_bucket (str): The destination bucket name.
|
|
340
341
|
dest_key (str): The destination object key.
|
|
341
|
-
|
|
342
|
+
|
|
342
343
|
Returns:
|
|
343
344
|
bool: True if the object was copied successfully.
|
|
344
345
|
Tags:
|
|
@@ -358,13 +359,13 @@ class AwsS3App(BaseApplication):
|
|
|
358
359
|
) -> bool:
|
|
359
360
|
"""
|
|
360
361
|
Moves an S3 object from a source to a destination. This is achieved by first copying the object to the new location and subsequently deleting the original. The move can occur within the same bucket or between different ones, returning `True` on success.
|
|
361
|
-
|
|
362
|
+
|
|
362
363
|
Args:
|
|
363
364
|
source_bucket (str): The source bucket name.
|
|
364
365
|
source_key (str): The source object key.
|
|
365
366
|
dest_bucket (str): The destination bucket name.
|
|
366
367
|
dest_key (str): The destination object key.
|
|
367
|
-
|
|
368
|
+
|
|
368
369
|
Returns:
|
|
369
370
|
bool: True if the object was moved successfully.
|
|
370
371
|
Tags:
|
|
@@ -377,11 +378,11 @@ class AwsS3App(BaseApplication):
|
|
|
377
378
|
def delete_single_object(self, bucket_name: str, key: str) -> bool:
|
|
378
379
|
"""
|
|
379
380
|
Deletes a single, specified object from an S3 bucket using its key. Returns `True` if successful, otherwise `False`. For bulk deletions in a single request, the `delete_objects` function should be used instead.
|
|
380
|
-
|
|
381
|
+
|
|
381
382
|
Args:
|
|
382
383
|
bucket_name (str): The name of the S3 bucket.
|
|
383
384
|
key (str): The key (path) to the object to delete.
|
|
384
|
-
|
|
385
|
+
|
|
385
386
|
Returns:
|
|
386
387
|
bool: True if the object was deleted successfully.
|
|
387
388
|
Tags:
|
|
@@ -396,11 +397,11 @@ class AwsS3App(BaseApplication):
|
|
|
396
397
|
def delete_objects(self, bucket_name: str, keys: list[str]) -> dict[str, Any]:
|
|
397
398
|
"""
|
|
398
399
|
Performs a bulk deletion of objects from a specified S3 bucket in a single request. Given a list of keys, it returns a dictionary detailing successful deletions and any errors. This method is the batch-processing counterpart to the singular `delete_object` function, designed for efficiency.
|
|
399
|
-
|
|
400
|
+
|
|
400
401
|
Args:
|
|
401
402
|
bucket_name (str): The name of the S3 bucket.
|
|
402
403
|
keys (List[str]): List of object keys to delete.
|
|
403
|
-
|
|
404
|
+
|
|
404
405
|
Returns:
|
|
405
406
|
Dict[str, Any]: Results of the deletion operation.
|
|
406
407
|
Tags:
|
|
@@ -427,13 +428,13 @@ class AwsS3App(BaseApplication):
|
|
|
427
428
|
) -> str:
|
|
428
429
|
"""
|
|
429
430
|
Generates a temporary, secure URL for a specific S3 object. This URL grants time-limited permissions for actions like GET, PUT, or DELETE, expiring after a defined period. It allows object access without sharing permanent AWS credentials.
|
|
430
|
-
|
|
431
|
+
|
|
431
432
|
Args:
|
|
432
433
|
bucket_name (str): The name of the S3 bucket.
|
|
433
434
|
key (str): The key (path) to the object.
|
|
434
435
|
expiration (int): Time in seconds for the presigned URL to remain valid (default: 3600).
|
|
435
436
|
http_method (str): HTTP method for the presigned URL (default: 'GET').
|
|
436
|
-
|
|
437
|
+
|
|
437
438
|
Returns:
|
|
438
439
|
str: The presigned URL or error message.
|
|
439
440
|
Tags:
|
|
@@ -465,14 +466,14 @@ class AwsS3App(BaseApplication):
|
|
|
465
466
|
) -> list[dict[str, Any]]:
|
|
466
467
|
"""
|
|
467
468
|
Filters objects within an S3 bucket and prefix based on a name pattern and size range. It retrieves all objects via `list_objects` and then applies the specified criteria client-side, returning a refined list of matching objects and their metadata.
|
|
468
|
-
|
|
469
|
+
|
|
469
470
|
Args:
|
|
470
471
|
bucket_name (str): The name of the S3 bucket.
|
|
471
472
|
prefix (str): The prefix to search within.
|
|
472
473
|
name_pattern (str): Pattern to match in object names (case-insensitive).
|
|
473
474
|
min_size (int, optional): Minimum object size in bytes.
|
|
474
475
|
max_size (int, optional): Maximum object size in bytes.
|
|
475
|
-
|
|
476
|
+
|
|
476
477
|
Returns:
|
|
477
478
|
List[Dict[str, Any]]: List of matching objects with metadata.
|
|
478
479
|
Tags:
|
|
@@ -499,11 +500,11 @@ class AwsS3App(BaseApplication):
|
|
|
499
500
|
def get_storage_summary(self, bucket_name: str, prefix: str = "") -> dict[str, Any]:
|
|
500
501
|
"""
|
|
501
502
|
Calculates and returns statistics for an S3 bucket or prefix. The result includes the total number of objects, their combined size in bytes, and a human-readable string representation of the size (e.g., '15.2 MB').
|
|
502
|
-
|
|
503
|
+
|
|
503
504
|
Args:
|
|
504
505
|
bucket_name (str): The name of the S3 bucket.
|
|
505
506
|
prefix (str): The prefix to calculate size for (default: entire bucket).
|
|
506
|
-
|
|
507
|
+
|
|
507
508
|
Returns:
|
|
508
509
|
Dict[str, Any]: Dictionary containing total size, object count, and human-readable size.
|
|
509
510
|
Tags:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Browser Use
|
|
File without changes
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
3
|
+
from universal_mcp.applications.application import APIApplication
|
|
4
|
+
from universal_mcp.integrations import Integration
|
|
5
|
+
|
|
6
|
+
from browser_use_sdk import BrowserUse
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BrowserUseApp(APIApplication):
|
|
10
|
+
def __init__(self, integration: Integration | None = None, **kwargs) -> None:
|
|
11
|
+
super().__init__(name="browser_use", integration=integration, **kwargs)
|
|
12
|
+
self._browser_client = None
|
|
13
|
+
|
|
14
|
+
@property
|
|
15
|
+
def browser_client(self) -> BrowserUse:
|
|
16
|
+
if self._browser_client is not None:
|
|
17
|
+
return self._browser_client
|
|
18
|
+
if not self.integration:
|
|
19
|
+
raise ValueError("Integration is required but not provided")
|
|
20
|
+
credentials = self.integration.get_credentials()
|
|
21
|
+
api_key = (
|
|
22
|
+
credentials.get("api_key")
|
|
23
|
+
or credentials.get("API_KEY")
|
|
24
|
+
or credentials.get("apiKey")
|
|
25
|
+
)
|
|
26
|
+
if not api_key:
|
|
27
|
+
raise ValueError("API key not found in integration credentials")
|
|
28
|
+
self._browser_client = BrowserUse(api_key=api_key)
|
|
29
|
+
return self._browser_client
|
|
30
|
+
|
|
31
|
+
async def browser_task(
|
|
32
|
+
self,
|
|
33
|
+
task: Annotated[str, "What you want the browser to do"],
|
|
34
|
+
max_steps: Annotated[int, "Max actions to take (1-10, default: 8)"] = 8,
|
|
35
|
+
llm: Annotated[str, "The LLM to use for the task"] = "gpt-4.1",
|
|
36
|
+
) -> dict:
|
|
37
|
+
"""
|
|
38
|
+
Creates and runs a browser automation task.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
task (str): The detailed description of the task for the browser to perform.
|
|
42
|
+
max_steps (int, optional): The maximum number of actions the browser can take. Defaults to 8.
|
|
43
|
+
llm (str, optional): The language model to use for interpreting the task. Defaults to "gpt-4.1".
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
dict: The result of the completed task, including output and other metadata.
|
|
47
|
+
"""
|
|
48
|
+
created_task = self.browser_client.tasks.create_task(
|
|
49
|
+
llm=llm, task=task, max_steps=max_steps
|
|
50
|
+
)
|
|
51
|
+
result = created_task.complete()
|
|
52
|
+
return result.to_dict()
|
|
53
|
+
|
|
54
|
+
async def get_browser_task_status(
|
|
55
|
+
self,
|
|
56
|
+
task_id: Annotated[str, "Task ID to check"],
|
|
57
|
+
) -> dict:
|
|
58
|
+
"""
|
|
59
|
+
Checks task progress with smart polling.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
task_id (str): The ID of the task to check.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
dict: The current status and details of the task.
|
|
66
|
+
"""
|
|
67
|
+
task = self.browser_client.tasks.get_task(task_id)
|
|
68
|
+
return task.to_dict()
|
|
69
|
+
|
|
70
|
+
def list_tools(self):
|
|
71
|
+
return [self.browser_task, self.get_browser_task_status]
|