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.
Files changed (103) hide show
  1. universal_mcp/applications/airtable/app.py +1 -0
  2. universal_mcp/applications/apollo/app.py +1 -0
  3. universal_mcp/applications/{aws-s3 → aws_s3}/app.py +4 -5
  4. universal_mcp/applications/bill/app.py +3 -3
  5. universal_mcp/applications/box/app.py +2 -6
  6. universal_mcp/applications/braze/app.py +2 -6
  7. universal_mcp/applications/cal_com_v2/__init__.py +1 -0
  8. universal_mcp/applications/{cal-com-v2 → cal_com_v2}/app.py +138 -182
  9. universal_mcp/applications/clickup/app.py +2 -2
  10. universal_mcp/applications/confluence/app.py +1 -0
  11. universal_mcp/applications/contentful/app.py +8 -19
  12. universal_mcp/applications/digitalocean/app.py +9 -27
  13. universal_mcp/applications/{domain-checker → domain_checker}/app.py +2 -1
  14. universal_mcp/applications/elevenlabs/app.py +98 -3188
  15. universal_mcp/applications/falai/app.py +1 -0
  16. universal_mcp/applications/file_system/__init__.py +1 -0
  17. universal_mcp/applications/file_system/app.py +96 -0
  18. universal_mcp/applications/fireflies/app.py +4 -3
  19. universal_mcp/applications/fpl/app.py +1 -0
  20. universal_mcp/applications/fpl/utils/fixtures.py +1 -1
  21. universal_mcp/applications/fpl/utils/helper.py +1 -1
  22. universal_mcp/applications/fpl/utils/position_utils.py +0 -1
  23. universal_mcp/applications/{ghost-content → ghost_content}/app.py +2 -1
  24. universal_mcp/applications/github/app.py +4 -3
  25. universal_mcp/applications/{google-calendar → google_calendar}/app.py +2 -1
  26. universal_mcp/applications/{google-docs → google_docs}/app.py +1 -1
  27. universal_mcp/applications/{google-drive → google_drive}/app.py +2 -1
  28. universal_mcp/applications/google_gemini/app.py +183 -0
  29. universal_mcp/applications/{google-mail → google_mail}/app.py +2 -1
  30. universal_mcp/applications/{google-searchconsole → google_searchconsole}/app.py +1 -1
  31. universal_mcp/applications/{google-sheet → google_sheet}/app.py +3 -2
  32. universal_mcp/applications/google_sheet/helper.py +385 -0
  33. universal_mcp/applications/hashnode/app.py +2 -1
  34. universal_mcp/applications/{http-tools → http_tools}/app.py +2 -1
  35. universal_mcp/applications/hubspot/app.py +16 -2
  36. universal_mcp/applications/jira/app.py +7 -18
  37. universal_mcp/applications/markitdown/app.py +2 -3
  38. universal_mcp/applications/{ms-teams → ms_teams}/app.py +1 -1
  39. universal_mcp/applications/openai/app.py +2 -3
  40. universal_mcp/applications/outlook/app.py +1 -3
  41. universal_mcp/applications/pipedrive/app.py +2 -6
  42. universal_mcp/applications/reddit/app.py +1 -0
  43. universal_mcp/applications/replicate/app.py +3 -3
  44. universal_mcp/applications/resend/app.py +1 -2
  45. universal_mcp/applications/rocketlane/app.py +1 -0
  46. universal_mcp/applications/semrush/app.py +478 -1467
  47. universal_mcp/applications/sentry/README.md +20 -20
  48. universal_mcp/applications/sentry/app.py +40 -40
  49. universal_mcp/applications/serpapi/app.py +2 -2
  50. universal_mcp/applications/sharepoint/app.py +2 -1
  51. universal_mcp/applications/shopify/app.py +1 -0
  52. universal_mcp/applications/slack/app.py +3 -3
  53. universal_mcp/applications/trello/app.py +9 -27
  54. universal_mcp/applications/twilio/__init__.py +1 -0
  55. universal_mcp/applications/{twillo → twilio}/app.py +2 -2
  56. universal_mcp/applications/twitter/README.md +1 -1
  57. universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +2 -2
  58. universal_mcp/applications/twitter/api_segments/lists_api.py +1 -1
  59. universal_mcp/applications/unipile/app.py +5 -1
  60. universal_mcp/applications/whatsapp/app.py +18 -17
  61. universal_mcp/applications/whatsapp/audio.py +110 -0
  62. universal_mcp/applications/whatsapp/whatsapp.py +398 -0
  63. universal_mcp/applications/{whatsapp-business → whatsapp_business}/app.py +1 -1
  64. universal_mcp/applications/youtube/app.py +195 -191
  65. universal_mcp/applications/zenquotes/app.py +1 -1
  66. {universal_mcp_applications-0.1.1.dist-info → universal_mcp_applications-0.1.3.dist-info}/METADATA +4 -2
  67. {universal_mcp_applications-0.1.1.dist-info → universal_mcp_applications-0.1.3.dist-info}/RECORD +97 -95
  68. universal_mcp/applications/cal-com-v2/__init__.py +0 -1
  69. universal_mcp/applications/google-ads/__init__.py +0 -1
  70. universal_mcp/applications/google-ads/app.py +0 -23
  71. universal_mcp/applications/google-gemini/app.py +0 -663
  72. universal_mcp/applications/twillo/README.md +0 -0
  73. universal_mcp/applications/twillo/__init__.py +0 -1
  74. /universal_mcp/applications/{aws-s3 → aws_s3}/README.md +0 -0
  75. /universal_mcp/applications/{aws-s3 → aws_s3}/__init__.py +0 -0
  76. /universal_mcp/applications/{cal-com-v2 → cal_com_v2}/README.md +0 -0
  77. /universal_mcp/applications/{domain-checker → domain_checker}/README.md +0 -0
  78. /universal_mcp/applications/{domain-checker → domain_checker}/__init__.py +0 -0
  79. /universal_mcp/applications/{ghost-content → ghost_content}/README.md +0 -0
  80. /universal_mcp/applications/{ghost-content → ghost_content}/__init__.py +0 -0
  81. /universal_mcp/applications/{google-calendar → google_calendar}/README.md +0 -0
  82. /universal_mcp/applications/{google-calendar → google_calendar}/__init__.py +0 -0
  83. /universal_mcp/applications/{google-docs → google_docs}/README.md +0 -0
  84. /universal_mcp/applications/{google-docs → google_docs}/__init__.py +0 -0
  85. /universal_mcp/applications/{google-drive → google_drive}/README.md +0 -0
  86. /universal_mcp/applications/{google-drive → google_drive}/__init__.py +0 -0
  87. /universal_mcp/applications/{google-gemini → google_gemini}/README.md +0 -0
  88. /universal_mcp/applications/{google-gemini → google_gemini}/__init__.py +0 -0
  89. /universal_mcp/applications/{google-mail → google_mail}/README.md +0 -0
  90. /universal_mcp/applications/{google-mail → google_mail}/__init__.py +0 -0
  91. /universal_mcp/applications/{google-searchconsole → google_searchconsole}/README.md +0 -0
  92. /universal_mcp/applications/{google-searchconsole → google_searchconsole}/__init__.py +0 -0
  93. /universal_mcp/applications/{google-sheet → google_sheet}/README.md +0 -0
  94. /universal_mcp/applications/{google-sheet → google_sheet}/__init__.py +0 -0
  95. /universal_mcp/applications/{http-tools → http_tools}/README.md +0 -0
  96. /universal_mcp/applications/{http-tools → http_tools}/__init__.py +0 -0
  97. /universal_mcp/applications/{ms-teams → ms_teams}/README.md +0 -0
  98. /universal_mcp/applications/{ms-teams → ms_teams}/__init__.py +0 -0
  99. /universal_mcp/applications/{google-ads → twilio}/README.md +0 -0
  100. /universal_mcp/applications/{whatsapp-business → whatsapp_business}/README.md +0 -0
  101. /universal_mcp/applications/{whatsapp-business → whatsapp_business}/__init__.py +0 -0
  102. {universal_mcp_applications-0.1.1.dist-info → universal_mcp_applications-0.1.3.dist-info}/WHEEL +0 -0
  103. {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 # For type hinting
1
+ from collections.abc import Callable
2
2
  from typing import Any
3
3
 
4
4
  from gql import gql
5
- from universal_mcp.applications import (
5
+
6
+ from universal_mcp.applications.application import (
6
7
  GraphQLApplication,
7
- ) # Assuming this is in a reachable path
8
+ )
8
9
  from universal_mcp.integrations import Integration
9
10
 
10
11
 
@@ -3,6 +3,7 @@ from datetime import datetime
3
3
  from typing import Any
4
4
 
5
5
  import requests
6
+
6
7
  from universal_mcp.applications.application import APIApplication
7
8
  from universal_mcp.integrations import Integration
8
9
 
@@ -3,7 +3,7 @@
3
3
  import logging
4
4
  from typing import Any
5
5
 
6
- from universal_mcp_fpl.utils.api import api
6
+ from .api import api
7
7
 
8
8
  # Set up logging following project conventions
9
9
  logger = logging.getLogger("fpl-mcp-server.fixtures")
@@ -3,7 +3,7 @@ import logging
3
3
  from typing import Any
4
4
 
5
5
  logger = logging.getLogger("fpl-mcp-server.fixtures")
6
- from universal_mcp_fpl.utils.api import api
6
+ from .api import api
7
7
 
8
8
  # Resources
9
9
 
@@ -1,6 +1,5 @@
1
1
  """Utilities for normalizing position terms in FPL context."""
2
2
 
3
-
4
3
  # Comprehensive position mapping dictionary
5
4
  POSITION_MAPPINGS = {
6
5
  # Standard FPL codes
@@ -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="ghost-content", integration=integration)
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 | None = None,
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.meta_root,
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="google-calendar", integration=integration)
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="google-docs", integration=integration)
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="google-drive", integration=integration)
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"![Image]({image_url})"
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="google-mail", integration=integration)
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="google-searchconsole", integration=integration, **kwargs)
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
- from universal_mcp_google_sheet.helper import (
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="google-sheet", integration=integration)
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]: