universal-mcp-applications 0.1.1__py3-none-any.whl → 0.1.3__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/airtable/app.py +1 -0
- universal_mcp/applications/apollo/app.py +1 -0
- universal_mcp/applications/{aws-s3 → aws_s3}/app.py +4 -5
- universal_mcp/applications/bill/app.py +3 -3
- universal_mcp/applications/box/app.py +2 -6
- universal_mcp/applications/braze/app.py +2 -6
- universal_mcp/applications/cal_com_v2/__init__.py +1 -0
- universal_mcp/applications/{cal-com-v2 → cal_com_v2}/app.py +138 -182
- universal_mcp/applications/clickup/app.py +2 -2
- universal_mcp/applications/confluence/app.py +1 -0
- universal_mcp/applications/contentful/app.py +8 -19
- universal_mcp/applications/digitalocean/app.py +9 -27
- universal_mcp/applications/{domain-checker → domain_checker}/app.py +2 -1
- universal_mcp/applications/elevenlabs/app.py +98 -3188
- universal_mcp/applications/falai/app.py +1 -0
- universal_mcp/applications/file_system/__init__.py +1 -0
- universal_mcp/applications/file_system/app.py +96 -0
- universal_mcp/applications/fireflies/app.py +4 -3
- universal_mcp/applications/fpl/app.py +1 -0
- universal_mcp/applications/fpl/utils/fixtures.py +1 -1
- universal_mcp/applications/fpl/utils/helper.py +1 -1
- universal_mcp/applications/fpl/utils/position_utils.py +0 -1
- universal_mcp/applications/{ghost-content → ghost_content}/app.py +2 -1
- universal_mcp/applications/github/app.py +4 -3
- universal_mcp/applications/{google-calendar → google_calendar}/app.py +2 -1
- universal_mcp/applications/{google-docs → google_docs}/app.py +1 -1
- universal_mcp/applications/{google-drive → google_drive}/app.py +2 -1
- universal_mcp/applications/google_gemini/app.py +183 -0
- universal_mcp/applications/{google-mail → google_mail}/app.py +2 -1
- universal_mcp/applications/{google-searchconsole → google_searchconsole}/app.py +1 -1
- universal_mcp/applications/{google-sheet → google_sheet}/app.py +3 -2
- universal_mcp/applications/google_sheet/helper.py +385 -0
- universal_mcp/applications/hashnode/app.py +2 -1
- universal_mcp/applications/{http-tools → http_tools}/app.py +2 -1
- universal_mcp/applications/hubspot/app.py +16 -2
- universal_mcp/applications/jira/app.py +7 -18
- universal_mcp/applications/markitdown/app.py +2 -3
- universal_mcp/applications/{ms-teams → ms_teams}/app.py +1 -1
- universal_mcp/applications/openai/app.py +2 -3
- universal_mcp/applications/outlook/app.py +1 -3
- universal_mcp/applications/pipedrive/app.py +2 -6
- universal_mcp/applications/reddit/app.py +1 -0
- universal_mcp/applications/replicate/app.py +3 -3
- universal_mcp/applications/resend/app.py +1 -2
- universal_mcp/applications/rocketlane/app.py +1 -0
- universal_mcp/applications/semrush/app.py +478 -1467
- universal_mcp/applications/sentry/README.md +20 -20
- universal_mcp/applications/sentry/app.py +40 -40
- universal_mcp/applications/serpapi/app.py +2 -2
- universal_mcp/applications/sharepoint/app.py +2 -1
- universal_mcp/applications/shopify/app.py +1 -0
- universal_mcp/applications/slack/app.py +3 -3
- universal_mcp/applications/trello/app.py +9 -27
- universal_mcp/applications/twilio/__init__.py +1 -0
- universal_mcp/applications/{twillo → twilio}/app.py +2 -2
- universal_mcp/applications/twitter/README.md +1 -1
- universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +2 -2
- universal_mcp/applications/twitter/api_segments/lists_api.py +1 -1
- universal_mcp/applications/unipile/app.py +5 -1
- universal_mcp/applications/whatsapp/app.py +18 -17
- universal_mcp/applications/whatsapp/audio.py +110 -0
- universal_mcp/applications/whatsapp/whatsapp.py +398 -0
- universal_mcp/applications/{whatsapp-business → whatsapp_business}/app.py +1 -1
- universal_mcp/applications/youtube/app.py +195 -191
- universal_mcp/applications/zenquotes/app.py +1 -1
- {universal_mcp_applications-0.1.1.dist-info → universal_mcp_applications-0.1.3.dist-info}/METADATA +4 -2
- {universal_mcp_applications-0.1.1.dist-info → universal_mcp_applications-0.1.3.dist-info}/RECORD +97 -95
- universal_mcp/applications/cal-com-v2/__init__.py +0 -1
- universal_mcp/applications/google-ads/__init__.py +0 -1
- universal_mcp/applications/google-ads/app.py +0 -23
- universal_mcp/applications/google-gemini/app.py +0 -663
- universal_mcp/applications/twillo/README.md +0 -0
- universal_mcp/applications/twillo/__init__.py +0 -1
- /universal_mcp/applications/{aws-s3 → aws_s3}/README.md +0 -0
- /universal_mcp/applications/{aws-s3 → aws_s3}/__init__.py +0 -0
- /universal_mcp/applications/{cal-com-v2 → cal_com_v2}/README.md +0 -0
- /universal_mcp/applications/{domain-checker → domain_checker}/README.md +0 -0
- /universal_mcp/applications/{domain-checker → domain_checker}/__init__.py +0 -0
- /universal_mcp/applications/{ghost-content → ghost_content}/README.md +0 -0
- /universal_mcp/applications/{ghost-content → ghost_content}/__init__.py +0 -0
- /universal_mcp/applications/{google-calendar → google_calendar}/README.md +0 -0
- /universal_mcp/applications/{google-calendar → google_calendar}/__init__.py +0 -0
- /universal_mcp/applications/{google-docs → google_docs}/README.md +0 -0
- /universal_mcp/applications/{google-docs → google_docs}/__init__.py +0 -0
- /universal_mcp/applications/{google-drive → google_drive}/README.md +0 -0
- /universal_mcp/applications/{google-drive → google_drive}/__init__.py +0 -0
- /universal_mcp/applications/{google-gemini → google_gemini}/README.md +0 -0
- /universal_mcp/applications/{google-gemini → google_gemini}/__init__.py +0 -0
- /universal_mcp/applications/{google-mail → google_mail}/README.md +0 -0
- /universal_mcp/applications/{google-mail → google_mail}/__init__.py +0 -0
- /universal_mcp/applications/{google-searchconsole → google_searchconsole}/README.md +0 -0
- /universal_mcp/applications/{google-searchconsole → google_searchconsole}/__init__.py +0 -0
- /universal_mcp/applications/{google-sheet → google_sheet}/README.md +0 -0
- /universal_mcp/applications/{google-sheet → google_sheet}/__init__.py +0 -0
- /universal_mcp/applications/{http-tools → http_tools}/README.md +0 -0
- /universal_mcp/applications/{http-tools → http_tools}/__init__.py +0 -0
- /universal_mcp/applications/{ms-teams → ms_teams}/README.md +0 -0
- /universal_mcp/applications/{ms-teams → ms_teams}/__init__.py +0 -0
- /universal_mcp/applications/{google-ads → twilio}/README.md +0 -0
- /universal_mcp/applications/{whatsapp-business → whatsapp_business}/README.md +0 -0
- /universal_mcp/applications/{whatsapp-business → whatsapp_business}/__init__.py +0 -0
- {universal_mcp_applications-0.1.1.dist-info → universal_mcp_applications-0.1.3.dist-info}/WHEEL +0 -0
- {universal_mcp_applications-0.1.1.dist-info → universal_mcp_applications-0.1.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -3,6 +3,7 @@ from typing import Any, Literal
|
|
|
3
3
|
|
|
4
4
|
from fal_client import AsyncClient, AsyncRequestHandle, Status
|
|
5
5
|
from loguru import logger
|
|
6
|
+
|
|
6
7
|
from universal_mcp.applications.application import APIApplication
|
|
7
8
|
from universal_mcp.exceptions import NotAuthorizedError, ToolError
|
|
8
9
|
from universal_mcp.integrations import Integration
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .app import FileSystemApp
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
5
|
+
from universal_mcp.applications.application import BaseApplication
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FileSystemApp(BaseApplication):
|
|
9
|
+
def __init__(self, **kwargs):
|
|
10
|
+
super().__init__(name="file_system", **kwargs)
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
async def _generate_file_path():
|
|
14
|
+
return f"/tmp/{uuid.uuid4()}"
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
async def read_file(file_path: str):
|
|
18
|
+
"""Reads file data from a file path.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
file_path (str): The path to the file to read.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
bytes: The file content as bytes.
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
FileNotFoundError: If the file doesn't exist.
|
|
28
|
+
IOError: If there's an error reading the file.
|
|
29
|
+
|
|
30
|
+
Tags:
|
|
31
|
+
important
|
|
32
|
+
"""
|
|
33
|
+
with open(file_path, "rb") as f:
|
|
34
|
+
return f.read()
|
|
35
|
+
|
|
36
|
+
@staticmethod
|
|
37
|
+
async def write_file(file_data: bytes, file_path: str = None):
|
|
38
|
+
"""Writes file data to a file path.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
file_data (bytes): The data to write to the file.
|
|
42
|
+
file_path (str, optional): The path where to write the file.
|
|
43
|
+
If None, generates a random path in /tmp. Defaults to None.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
dict: A dictionary containing the operation result with keys:
|
|
47
|
+
- status (str): "success" if the operation completed successfully
|
|
48
|
+
- data (dict): Contains file information with keys:
|
|
49
|
+
- url (str): The file path where the data was written
|
|
50
|
+
- filename (str): The filename (same as url in this implementation)
|
|
51
|
+
- size (int): The size of the written data in bytes
|
|
52
|
+
|
|
53
|
+
Raises:
|
|
54
|
+
IOError: If there's an error writing the file.
|
|
55
|
+
PermissionError: If there are insufficient permissions to write to the path.
|
|
56
|
+
|
|
57
|
+
Tags:
|
|
58
|
+
important
|
|
59
|
+
"""
|
|
60
|
+
if file_path is None:
|
|
61
|
+
file_path = await FileSystemApp._generate_file_path()
|
|
62
|
+
with open(file_path, "wb") as f:
|
|
63
|
+
f.write(file_data)
|
|
64
|
+
result = {
|
|
65
|
+
"status": "success",
|
|
66
|
+
"data": {
|
|
67
|
+
"url": file_path,
|
|
68
|
+
"filename": file_path,
|
|
69
|
+
"size": len(file_data),
|
|
70
|
+
},
|
|
71
|
+
}
|
|
72
|
+
return result
|
|
73
|
+
|
|
74
|
+
@staticmethod
|
|
75
|
+
async def delete_file(file_path: str):
|
|
76
|
+
"""Deletes a file from the file system."""
|
|
77
|
+
os.remove(file_path)
|
|
78
|
+
return {"status": "success"}
|
|
79
|
+
|
|
80
|
+
@staticmethod
|
|
81
|
+
async def move_file(source_file_path: str, dest_file_path: str):
|
|
82
|
+
"""Moves a file from one path to another."""
|
|
83
|
+
os.rename(source_file_path, dest_file_path)
|
|
84
|
+
return {"status": "success"}
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
async def copy_file(source_file_path: str, dest_file_path: str):
|
|
88
|
+
"""Copies a file from one path to another."""
|
|
89
|
+
shutil.copy(source_file_path, dest_file_path)
|
|
90
|
+
return {"status": "success"}
|
|
91
|
+
|
|
92
|
+
def list_tools(self):
|
|
93
|
+
return [
|
|
94
|
+
FileSystemApp.read_file,
|
|
95
|
+
FileSystemApp.write_file,
|
|
96
|
+
]
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
from collections.abc import Callable
|
|
1
|
+
from collections.abc import Callable
|
|
2
2
|
from typing import Any
|
|
3
3
|
|
|
4
4
|
from gql import gql
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
from universal_mcp.applications.application import (
|
|
6
7
|
GraphQLApplication,
|
|
7
|
-
)
|
|
8
|
+
)
|
|
8
9
|
from universal_mcp.integrations import Integration
|
|
9
10
|
|
|
10
11
|
|
|
@@ -2,6 +2,7 @@ from collections.abc import Callable
|
|
|
2
2
|
from typing import Any
|
|
3
3
|
|
|
4
4
|
from loguru import logger
|
|
5
|
+
|
|
5
6
|
from universal_mcp.applications.application import APIApplication
|
|
6
7
|
from universal_mcp.integrations import Integration
|
|
7
8
|
|
|
@@ -24,7 +25,7 @@ class GhostContentApp(APIApplication):
|
|
|
24
25
|
"https://your-ghost-site.com") and 'key' (the Content API key)
|
|
25
26
|
via `integration.get_credentials()`.
|
|
26
27
|
"""
|
|
27
|
-
super().__init__(name="
|
|
28
|
+
super().__init__(name="ghost_content", integration=integration)
|
|
28
29
|
self._base_url = None
|
|
29
30
|
self._api_key = None # Cache the API key
|
|
30
31
|
self._version = None # Cache the version
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from typing import Any
|
|
2
2
|
|
|
3
3
|
from loguru import logger
|
|
4
|
+
|
|
4
5
|
from universal_mcp.applications.application import APIApplication
|
|
5
6
|
from universal_mcp.integrations import Integration
|
|
6
7
|
|
|
@@ -4469,7 +4470,8 @@ class GithubApp(APIApplication):
|
|
|
4469
4470
|
dependabot_security_updates_enabled_for_new_repositories: bool | None = None,
|
|
4470
4471
|
dependency_graph_enabled_for_new_repositories: bool | None = None,
|
|
4471
4472
|
secret_scanning_enabled_for_new_repositories: bool | None = None,
|
|
4472
|
-
secret_scanning_push_protection_enabled_for_new_repositories: bool
|
|
4473
|
+
secret_scanning_push_protection_enabled_for_new_repositories: bool
|
|
4474
|
+
| None = None,
|
|
4473
4475
|
secret_scanning_push_protection_custom_link_enabled: bool | None = None,
|
|
4474
4476
|
secret_scanning_push_protection_custom_link: str | None = None,
|
|
4475
4477
|
deploy_keys_enabled_for_repositories: bool | None = None,
|
|
@@ -49574,7 +49576,7 @@ class GithubApp(APIApplication):
|
|
|
49574
49576
|
self.update_issue,
|
|
49575
49577
|
self.list_repo_activities,
|
|
49576
49578
|
# Auto Generated from open api spec
|
|
49577
|
-
self.
|
|
49579
|
+
self.meta_get,
|
|
49578
49580
|
self.list_advisories,
|
|
49579
49581
|
self.get_advisory_by_id,
|
|
49580
49582
|
self.apps_get_authenticated,
|
|
@@ -49654,7 +49656,6 @@ class GithubApp(APIApplication):
|
|
|
49654
49656
|
self.get_stubbed_account,
|
|
49655
49657
|
self.apps_list_plans_stubbed,
|
|
49656
49658
|
self.get_plan_accounts,
|
|
49657
|
-
self.meta_get,
|
|
49658
49659
|
self.get_network_repo_events,
|
|
49659
49660
|
self.get_notifications,
|
|
49660
49661
|
self.update_notification,
|
|
@@ -2,13 +2,14 @@ from datetime import UTC, datetime, timedelta
|
|
|
2
2
|
from typing import Any
|
|
3
3
|
|
|
4
4
|
from loguru import logger
|
|
5
|
+
|
|
5
6
|
from universal_mcp.applications.application import APIApplication
|
|
6
7
|
from universal_mcp.integrations import Integration
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
class GoogleCalendarApp(APIApplication):
|
|
10
11
|
def __init__(self, integration: Integration) -> None:
|
|
11
|
-
super().__init__(name="
|
|
12
|
+
super().__init__(name="google_calendar", integration=integration)
|
|
12
13
|
self.base_api_url = "https://www.googleapis.com/calendar/v3/calendars/primary"
|
|
13
14
|
self.base_url = "https://www.googleapis.com/calendar/v3"
|
|
14
15
|
|
|
@@ -6,7 +6,7 @@ from universal_mcp.integrations import Integration
|
|
|
6
6
|
|
|
7
7
|
class GoogleDocsApp(APIApplication):
|
|
8
8
|
def __init__(self, integration: Integration) -> None:
|
|
9
|
-
super().__init__(name="
|
|
9
|
+
super().__init__(name="google_docs", integration=integration)
|
|
10
10
|
self.base_api_url = "https://docs.googleapis.com/v1/documents"
|
|
11
11
|
|
|
12
12
|
def create_document(self, title: str) -> dict[str, Any]:
|
|
@@ -2,6 +2,7 @@ from typing import Any
|
|
|
2
2
|
|
|
3
3
|
import httpx
|
|
4
4
|
from loguru import logger
|
|
5
|
+
|
|
5
6
|
from universal_mcp.applications.application import APIApplication
|
|
6
7
|
from universal_mcp.integrations import Integration
|
|
7
8
|
|
|
@@ -13,7 +14,7 @@ class GoogleDriveApp(APIApplication):
|
|
|
13
14
|
"""
|
|
14
15
|
|
|
15
16
|
def __init__(self, integration: Integration | None = None) -> None:
|
|
16
|
-
super().__init__(name="
|
|
17
|
+
super().__init__(name="google_drive", integration=integration)
|
|
17
18
|
self.base_url = "https://www.googleapis.com/drive/v3"
|
|
18
19
|
|
|
19
20
|
def move_files(
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
import wave
|
|
3
|
+
from typing import Annotated # Added Literal for type hinting
|
|
4
|
+
|
|
5
|
+
from google import genai
|
|
6
|
+
from google.genai import types
|
|
7
|
+
from loguru import logger
|
|
8
|
+
from PIL import Image
|
|
9
|
+
|
|
10
|
+
from universal_mcp.applications.application import APIApplication
|
|
11
|
+
from universal_mcp.applications.file_system.app import FileSystemApp
|
|
12
|
+
from universal_mcp.integrations import Integration
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class GoogleGeminiApp(APIApplication):
|
|
16
|
+
def __init__(self, integration: Integration = None, **kwargs) -> None:
|
|
17
|
+
super().__init__(name="google_gemini", integration=integration, **kwargs)
|
|
18
|
+
self._genai_client = None
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def genai_client(self) -> genai.Client:
|
|
22
|
+
if self._genai_client is not None:
|
|
23
|
+
return self._genai_client
|
|
24
|
+
credentials = self.integration.get_credentials()
|
|
25
|
+
api_key = (
|
|
26
|
+
credentials.get("api_key")
|
|
27
|
+
or credentials.get("API_KEY")
|
|
28
|
+
or credentials.get("apiKey")
|
|
29
|
+
)
|
|
30
|
+
if not api_key:
|
|
31
|
+
raise ValueError("API key not found in integration credentials")
|
|
32
|
+
self._genai_client = genai.Client(api_key=api_key)
|
|
33
|
+
return self._genai_client
|
|
34
|
+
|
|
35
|
+
async def generate_text(
|
|
36
|
+
self,
|
|
37
|
+
prompt: Annotated[str, "The prompt to generate text from"],
|
|
38
|
+
model: str = "gemini-2.5-flash",
|
|
39
|
+
) -> str:
|
|
40
|
+
"""Generates text using the Google Gemini model.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
prompt (str): The prompt to generate text from.
|
|
44
|
+
model (str, optional): The Gemini model to use for text generation. Defaults to "gemini-2.5-flash".
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
str: The generated text response from the Gemini model.
|
|
48
|
+
|
|
49
|
+
Raises:
|
|
50
|
+
ValueError: If the API key is not found in the integration credentials.
|
|
51
|
+
Exception: If the underlying client or API call fails.
|
|
52
|
+
|
|
53
|
+
Example:
|
|
54
|
+
response = app.generate_text("Tell me a joke.")
|
|
55
|
+
|
|
56
|
+
Tags:
|
|
57
|
+
important
|
|
58
|
+
"""
|
|
59
|
+
response = self.genai_client.generate_content(prompt, model=model)
|
|
60
|
+
return response.text
|
|
61
|
+
|
|
62
|
+
async def generate_image(
|
|
63
|
+
self,
|
|
64
|
+
prompt: Annotated[str, "The prompt to generate image from"],
|
|
65
|
+
image: Annotated[str, "The reference image path"] | None = None,
|
|
66
|
+
model: str = "gemini-2.5-flash-image-preview",
|
|
67
|
+
) -> list:
|
|
68
|
+
"""
|
|
69
|
+
Generates an image using the Google Gemini model and returns a list of results.
|
|
70
|
+
Each result is a dict with either 'text' or 'image_bytes' (raw image data).
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
prompt (str): The prompt to generate image from.
|
|
74
|
+
model (str, optional): The Gemini model to use for image generation. Defaults to "gemini-2.5-flash-image-preview".
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
list: A list of dicts, each containing either 'text' or 'image_bytes'.
|
|
78
|
+
|
|
79
|
+
Tags:
|
|
80
|
+
important
|
|
81
|
+
"""
|
|
82
|
+
# The Gemini API is synchronous, so run in a thread
|
|
83
|
+
contents = [prompt]
|
|
84
|
+
if image:
|
|
85
|
+
image = Image.open(image)
|
|
86
|
+
contents.append(image)
|
|
87
|
+
response = self.genai_client.models.generate_content(
|
|
88
|
+
model=model,
|
|
89
|
+
contents=contents,
|
|
90
|
+
)
|
|
91
|
+
candidate = response.candidates[0]
|
|
92
|
+
text = ""
|
|
93
|
+
for part in candidate.content.parts:
|
|
94
|
+
if part.text is not None:
|
|
95
|
+
text += part.text
|
|
96
|
+
elif part.inline_data is not None:
|
|
97
|
+
# Return the raw image bytes
|
|
98
|
+
image_bytes = part.inline_data.data
|
|
99
|
+
upload_result = await FileSystemApp.write_file(
|
|
100
|
+
image_bytes, f"/tmp/{uuid.uuid4()}.png"
|
|
101
|
+
)
|
|
102
|
+
logger.info(f"Upload result: {upload_result['status']}")
|
|
103
|
+
image_url = upload_result["data"]["url"]
|
|
104
|
+
logger.info(f"Image URL: {image_url}")
|
|
105
|
+
text += f""
|
|
106
|
+
logger.info(f"Text: {text}")
|
|
107
|
+
return {"text": text}
|
|
108
|
+
|
|
109
|
+
async def generate_audio(
|
|
110
|
+
self,
|
|
111
|
+
prompt: Annotated[str, "The prompt to generate audio from"],
|
|
112
|
+
model: str = "gemini-2.5-flash-preview-tts",
|
|
113
|
+
) -> str:
|
|
114
|
+
"""Generates audio using the Google Gemini model and returns the uploaded audio URL.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
prompt (str): The prompt to generate audio from.
|
|
118
|
+
model (str, optional): The Gemini model to use for audio generation. Defaults to "gemini-2.5-flash-preview-tts".
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
str: The URL of the uploaded audio file.
|
|
122
|
+
|
|
123
|
+
Tags:
|
|
124
|
+
important
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
# Set up the wave file to save the output:
|
|
128
|
+
def wave_file(filename, pcm, channels=1, rate=24000, sample_width=2):
|
|
129
|
+
with wave.open(filename, "wb") as wf:
|
|
130
|
+
wf.setnchannels(channels)
|
|
131
|
+
wf.setsampwidth(sample_width)
|
|
132
|
+
wf.setframerate(rate)
|
|
133
|
+
wf.writeframes(pcm)
|
|
134
|
+
|
|
135
|
+
response = self.genai_client.models.generate_content(
|
|
136
|
+
model=model,
|
|
137
|
+
contents=prompt,
|
|
138
|
+
config=types.GenerateContentConfig(
|
|
139
|
+
response_modalities=["AUDIO"],
|
|
140
|
+
speech_config=types.SpeechConfig(
|
|
141
|
+
voice_config=types.VoiceConfig(
|
|
142
|
+
prebuilt_voice_config=types.PrebuiltVoiceConfig(
|
|
143
|
+
voice_name="Kore",
|
|
144
|
+
)
|
|
145
|
+
)
|
|
146
|
+
),
|
|
147
|
+
),
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
data = response.candidates[0].content.parts[0].inline_data.data
|
|
151
|
+
|
|
152
|
+
file_name = "/tmp/audio.wav"
|
|
153
|
+
wave_file(file_name, data) # Saves the file to current directory
|
|
154
|
+
# Upload the audio file directly
|
|
155
|
+
upload_result = await FileSystemApp.move_file(
|
|
156
|
+
file_name, f"/tmp/{uuid.uuid4()}.wav"
|
|
157
|
+
)
|
|
158
|
+
logger.info(f"Audio upload result: {upload_result['status']}")
|
|
159
|
+
audio_url = upload_result["data"]["url"]
|
|
160
|
+
logger.info(f"Audio URL: {audio_url}")
|
|
161
|
+
|
|
162
|
+
return audio_url
|
|
163
|
+
|
|
164
|
+
def list_tools(self):
|
|
165
|
+
return [
|
|
166
|
+
self.generate_text,
|
|
167
|
+
self.generate_image,
|
|
168
|
+
self.generate_audio,
|
|
169
|
+
]
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
async def test_google_gemini():
|
|
173
|
+
app = GoogleGeminiApp()
|
|
174
|
+
result = await app.generate_image(
|
|
175
|
+
"A beautiful women potrait with red green hair color"
|
|
176
|
+
)
|
|
177
|
+
print(result)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
if __name__ == "__main__":
|
|
181
|
+
import asyncio
|
|
182
|
+
|
|
183
|
+
asyncio.run(test_google_gemini())
|
|
@@ -4,13 +4,14 @@ from email.message import EmailMessage
|
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
6
|
from loguru import logger
|
|
7
|
+
|
|
7
8
|
from universal_mcp.applications.application import APIApplication
|
|
8
9
|
from universal_mcp.integrations import Integration
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class GoogleMailApp(APIApplication):
|
|
12
13
|
def __init__(self, integration: Integration) -> None:
|
|
13
|
-
super().__init__(name="
|
|
14
|
+
super().__init__(name="google_mail", integration=integration)
|
|
14
15
|
self.base_api_url = "https://gmail.googleapis.com/gmail/v1/users/me"
|
|
15
16
|
self.base_url = "https://gmail.googleapis.com"
|
|
16
17
|
|
|
@@ -10,7 +10,7 @@ logger = logging.getLogger(__name__)
|
|
|
10
10
|
|
|
11
11
|
class GoogleSearchconsoleApp(APIApplication):
|
|
12
12
|
def __init__(self, integration: Integration = None, **kwargs) -> None:
|
|
13
|
-
super().__init__(name="
|
|
13
|
+
super().__init__(name="google_searchconsole", integration=integration, **kwargs)
|
|
14
14
|
self.webmasters_base_url = "https://www.googleapis.com/webmasters/v3"
|
|
15
15
|
self.searchconsole_base_url = "https://searchconsole.googleapis.com/v1"
|
|
16
16
|
|
|
@@ -2,7 +2,8 @@ from typing import Any
|
|
|
2
2
|
|
|
3
3
|
from universal_mcp.applications.application import APIApplication
|
|
4
4
|
from universal_mcp.integrations import Integration
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
from .helper import (
|
|
6
7
|
analyze_sheet_for_tables,
|
|
7
8
|
analyze_table_schema,
|
|
8
9
|
)
|
|
@@ -15,7 +16,7 @@ class GoogleSheetApp(APIApplication):
|
|
|
15
16
|
"""
|
|
16
17
|
|
|
17
18
|
def __init__(self, integration: Integration | None = None) -> None:
|
|
18
|
-
super().__init__(name="
|
|
19
|
+
super().__init__(name="google_sheet", integration=integration)
|
|
19
20
|
self.base_url = "https://sheets.googleapis.com/v4/spreadsheets"
|
|
20
21
|
|
|
21
22
|
def create_spreadsheet(self, title: str) -> dict[str, Any]:
|