universal-mcp-applications 0.1.39rc8__py3-none-any.whl → 0.1.39rc16__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 +1 -1
- universal_mcp/applications/airtable/app.py +13 -13
- universal_mcp/applications/apollo/app.py +2 -2
- universal_mcp/applications/aws_s3/app.py +30 -19
- universal_mcp/applications/browser_use/app.py +10 -7
- universal_mcp/applications/contentful/app.py +4 -4
- universal_mcp/applications/crustdata/app.py +2 -2
- universal_mcp/applications/e2b/app.py +3 -4
- universal_mcp/applications/elevenlabs/README.md +27 -3
- universal_mcp/applications/elevenlabs/app.py +753 -48
- universal_mcp/applications/exa/app.py +18 -11
- universal_mcp/applications/falai/README.md +5 -7
- universal_mcp/applications/falai/app.py +160 -159
- universal_mcp/applications/firecrawl/app.py +14 -15
- universal_mcp/applications/ghost_content/app.py +4 -4
- universal_mcp/applications/github/app.py +2 -2
- universal_mcp/applications/gong/app.py +2 -2
- universal_mcp/applications/google_docs/README.md +15 -14
- universal_mcp/applications/google_docs/app.py +5 -4
- universal_mcp/applications/google_gemini/app.py +61 -17
- universal_mcp/applications/google_sheet/README.md +2 -1
- universal_mcp/applications/google_sheet/app.py +55 -0
- universal_mcp/applications/heygen/README.md +10 -32
- universal_mcp/applications/heygen/app.py +350 -744
- universal_mcp/applications/klaviyo/app.py +2 -2
- universal_mcp/applications/linkedin/README.md +14 -2
- universal_mcp/applications/linkedin/app.py +411 -38
- universal_mcp/applications/ms_teams/app.py +420 -1285
- universal_mcp/applications/notion/app.py +2 -2
- universal_mcp/applications/openai/app.py +1 -1
- universal_mcp/applications/perplexity/app.py +6 -7
- universal_mcp/applications/reddit/app.py +4 -4
- universal_mcp/applications/resend/app.py +31 -32
- universal_mcp/applications/rocketlane/app.py +2 -2
- universal_mcp/applications/scraper/app.py +51 -21
- universal_mcp/applications/semrush/app.py +1 -1
- universal_mcp/applications/serpapi/app.py +8 -7
- universal_mcp/applications/shopify/app.py +5 -7
- universal_mcp/applications/shortcut/app.py +3 -2
- universal_mcp/applications/slack/app.py +2 -2
- universal_mcp/applications/twilio/app.py +14 -13
- {universal_mcp_applications-0.1.39rc8.dist-info → universal_mcp_applications-0.1.39rc16.dist-info}/METADATA +1 -1
- {universal_mcp_applications-0.1.39rc8.dist-info → universal_mcp_applications-0.1.39rc16.dist-info}/RECORD +45 -45
- {universal_mcp_applications-0.1.39rc8.dist-info → universal_mcp_applications-0.1.39rc16.dist-info}/WHEEL +0 -0
- {universal_mcp_applications-0.1.39rc8.dist-info → universal_mcp_applications-0.1.39rc16.dist-info}/licenses/LICENSE +0 -0
|
@@ -27,8 +27,7 @@ class FirecrawlApp(APIApplication):
|
|
|
27
27
|
if FirecrawlApiClient is None:
|
|
28
28
|
logger.warning("Firecrawl SDK is not available. Firecrawl tools will not function.")
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
def firecrawl_api_key(self) -> str:
|
|
30
|
+
async def get_firecrawl_api_key(self) -> str:
|
|
32
31
|
"""
|
|
33
32
|
A property that lazily retrieves and caches the Firecrawl API key from the configured integration. On first access, it fetches credentials and raises a `NotAuthorizedError` if the key is unobtainable, ensuring all subsequent API calls within the application are properly authenticated before execution.
|
|
34
33
|
"""
|
|
@@ -37,7 +36,7 @@ class FirecrawlApp(APIApplication):
|
|
|
37
36
|
logger.error(f"{self.name.capitalize()} App: Integration not configured.")
|
|
38
37
|
raise NotAuthorizedError(f"Integration not configured for {self.name.capitalize()} App. Cannot retrieve API key.")
|
|
39
38
|
try:
|
|
40
|
-
credentials = self.integration.
|
|
39
|
+
credentials = await self.integration.get_credentials_async()
|
|
41
40
|
except NotAuthorizedError as e:
|
|
42
41
|
logger.error(f"{self.name.capitalize()} App: Authorization error when fetching credentials: {e.message}")
|
|
43
42
|
raise
|
|
@@ -65,7 +64,7 @@ class FirecrawlApp(APIApplication):
|
|
|
65
64
|
assert self._firecrawl_api_key is not None
|
|
66
65
|
return self._firecrawl_api_key
|
|
67
66
|
|
|
68
|
-
def
|
|
67
|
+
async def get_firecrawl_client(self) -> AsyncFirecrawl:
|
|
69
68
|
"""
|
|
70
69
|
Initializes and returns the Firecrawl client after ensuring API key is set.
|
|
71
70
|
Raises NotAuthorizedError if API key cannot be obtained or SDK is not installed.
|
|
@@ -73,7 +72,7 @@ class FirecrawlApp(APIApplication):
|
|
|
73
72
|
if FirecrawlApiClient is None:
|
|
74
73
|
logger.error("Firecrawl SDK (firecrawl-py) is not available.")
|
|
75
74
|
raise ToolError("Firecrawl SDK (firecrawl-py) is not installed or failed to import.")
|
|
76
|
-
current_api_key = self.
|
|
75
|
+
current_api_key = await self.get_firecrawl_api_key()
|
|
77
76
|
return FirecrawlApiClient(api_key=current_api_key)
|
|
78
77
|
|
|
79
78
|
def _handle_firecrawl_exception(self, e: Exception, operation_desc: str) -> str:
|
|
@@ -161,7 +160,7 @@ class FirecrawlApp(APIApplication):
|
|
|
161
160
|
"""
|
|
162
161
|
logger.info(f"Attempting to scrape URL: {url} with schema: {schema is not None}, prompt: {prompt is not None}")
|
|
163
162
|
try:
|
|
164
|
-
client = self.
|
|
163
|
+
client = await self.get_firecrawl_client()
|
|
165
164
|
|
|
166
165
|
# Construct formats if schema or prompt is provided (V2 structured output)
|
|
167
166
|
if schema or prompt:
|
|
@@ -212,7 +211,7 @@ class FirecrawlApp(APIApplication):
|
|
|
212
211
|
"""
|
|
213
212
|
logger.info(f"Attempting Firecrawl search for query: {query}")
|
|
214
213
|
try:
|
|
215
|
-
client = self.
|
|
214
|
+
client = await self.get_firecrawl_client()
|
|
216
215
|
response = await client.search(query=query)
|
|
217
216
|
logger.info(f"Successfully performed Firecrawl search for query: {query}")
|
|
218
217
|
return self._to_serializable(response)
|
|
@@ -250,7 +249,7 @@ class FirecrawlApp(APIApplication):
|
|
|
250
249
|
"""
|
|
251
250
|
logger.info(f"Attempting to start Firecrawl crawl for URL: {url} with limit: {limit}")
|
|
252
251
|
try:
|
|
253
|
-
client = self.
|
|
252
|
+
client = await self.get_firecrawl_client()
|
|
254
253
|
response = await client.start_crawl(url=url, limit=limit, scrape_options=scrape_options)
|
|
255
254
|
job_id = response.id
|
|
256
255
|
logger.info(f"Successfully started Firecrawl crawl for URL {url}, Job ID: {job_id}")
|
|
@@ -282,7 +281,7 @@ class FirecrawlApp(APIApplication):
|
|
|
282
281
|
"""
|
|
283
282
|
logger.info(f"Attempting to check Firecrawl crawl status for job ID: {job_id}")
|
|
284
283
|
try:
|
|
285
|
-
client = self.
|
|
284
|
+
client = await self.get_firecrawl_client()
|
|
286
285
|
status = await client.get_crawl_status(job_id=job_id)
|
|
287
286
|
logger.info(f"Successfully checked Firecrawl crawl status for job ID: {job_id}")
|
|
288
287
|
return self._to_serializable(status)
|
|
@@ -314,7 +313,7 @@ class FirecrawlApp(APIApplication):
|
|
|
314
313
|
"""
|
|
315
314
|
logger.info(f"Attempting to cancel Firecrawl crawl job ID: {job_id}")
|
|
316
315
|
try:
|
|
317
|
-
client = self.
|
|
316
|
+
client = await self.get_firecrawl_client()
|
|
318
317
|
response = await client.cancel_crawl(crawl_id=job_id)
|
|
319
318
|
logger.info(f"Successfully issued cancel command for Firecrawl crawl job ID: {job_id}")
|
|
320
319
|
return self._to_serializable(response)
|
|
@@ -345,7 +344,7 @@ class FirecrawlApp(APIApplication):
|
|
|
345
344
|
"""
|
|
346
345
|
logger.info(f"Attempting to start Firecrawl batch scrape for {len(urls)} URLs.")
|
|
347
346
|
try:
|
|
348
|
-
client = self.
|
|
347
|
+
client = await self.get_firecrawl_client()
|
|
349
348
|
response = await client.start_batch_scrape(urls=urls)
|
|
350
349
|
logger.info(f"Successfully started Firecrawl batch scrape for {len(urls)} URLs.")
|
|
351
350
|
return self._to_serializable(response)
|
|
@@ -376,7 +375,7 @@ class FirecrawlApp(APIApplication):
|
|
|
376
375
|
"""
|
|
377
376
|
logger.info(f"Attempting to check Firecrawl batch scrape status for job ID: {job_id}")
|
|
378
377
|
try:
|
|
379
|
-
client = self.
|
|
378
|
+
client = await self.get_firecrawl_client()
|
|
380
379
|
status = await client.get_batch_scrape_status(job_id=job_id)
|
|
381
380
|
logger.info(f"Successfully checked Firecrawl batch scrape status for job ID: {job_id}")
|
|
382
381
|
return self._to_serializable(status)
|
|
@@ -448,7 +447,7 @@ class FirecrawlApp(APIApplication):
|
|
|
448
447
|
f"Attempting quick web extraction for {len(urls)} URLs with prompt: {prompt is not None}, schema: {schema is not None}."
|
|
449
448
|
)
|
|
450
449
|
try:
|
|
451
|
-
client = self.
|
|
450
|
+
client = await self.get_firecrawl_client()
|
|
452
451
|
response = await client.extract(
|
|
453
452
|
urls=urls, prompt=prompt, schema=schema, system_prompt=system_prompt, allow_external_links=allow_external_links
|
|
454
453
|
)
|
|
@@ -488,7 +487,7 @@ class FirecrawlApp(APIApplication):
|
|
|
488
487
|
"""
|
|
489
488
|
logger.info(f"Attempting to check Firecrawl extraction status for job ID: {job_id}")
|
|
490
489
|
try:
|
|
491
|
-
client = self.
|
|
490
|
+
client = await self.get_firecrawl_client()
|
|
492
491
|
status = await client.get_extract_status(job_id=job_id)
|
|
493
492
|
logger.info(f"Successfully checked Firecrawl extraction status for job ID: {job_id}")
|
|
494
493
|
return self._to_serializable(status)
|
|
@@ -520,7 +519,7 @@ class FirecrawlApp(APIApplication):
|
|
|
520
519
|
"""
|
|
521
520
|
logger.info(f"Attempting to map site: {url} with limit: {limit}")
|
|
522
521
|
try:
|
|
523
|
-
client = self.
|
|
522
|
+
client = await self.get_firecrawl_client()
|
|
524
523
|
# client.map signature (async): (url, search=None, ignoreSitemap=None, includeSubdomains=None, limit=None)
|
|
525
524
|
# We expose url and limit for now, maybe more if needed later.
|
|
526
525
|
response = await client.map(url=url, limit=limit)
|
|
@@ -21,7 +21,7 @@ class GhostContentApp(APIApplication):
|
|
|
21
21
|
and Content API key.
|
|
22
22
|
It is expected that the integration provides 'url' (e.g.,
|
|
23
23
|
"https://your-ghost-site.com") and 'key' (the Content API key)
|
|
24
|
-
via `integration.
|
|
24
|
+
via `integration.get_credentials_async()`.
|
|
25
25
|
"""
|
|
26
26
|
super().__init__(name="ghost_content", integration=integration)
|
|
27
27
|
self._base_url = None
|
|
@@ -35,7 +35,7 @@ class GhostContentApp(APIApplication):
|
|
|
35
35
|
This is constructed from the integration's credentials.
|
|
36
36
|
"""
|
|
37
37
|
if not self._base_url:
|
|
38
|
-
credentials = self.integration.
|
|
38
|
+
credentials = await self.integration.get_credentials_async()
|
|
39
39
|
ghost_url = credentials.get("url") or credentials.get("admin_domain")
|
|
40
40
|
if not ghost_url:
|
|
41
41
|
logger.error("GhostContentApp: Missing 'url' or 'admin_domain' in integration credentials.")
|
|
@@ -63,7 +63,7 @@ class GhostContentApp(APIApplication):
|
|
|
63
63
|
Caches the key after the first retrieval.
|
|
64
64
|
"""
|
|
65
65
|
if not self._api_key:
|
|
66
|
-
credentials = self.integration.
|
|
66
|
+
credentials = await self.integration.get_credentials_async()
|
|
67
67
|
api_key = credentials.get("key") or credentials.get("api_key") or credentials.get("API_KEY")
|
|
68
68
|
if not api_key:
|
|
69
69
|
logger.error("GhostContentApp: Content API key ('key') not found in integration credentials.")
|
|
@@ -78,7 +78,7 @@ class GhostContentApp(APIApplication):
|
|
|
78
78
|
Caches the version after the first retrieval.
|
|
79
79
|
"""
|
|
80
80
|
if not self._version:
|
|
81
|
-
credentials = self.integration.
|
|
81
|
+
credentials = await self.integration.get_credentials_async()
|
|
82
82
|
version = credentials.get("api_version")
|
|
83
83
|
if not version:
|
|
84
84
|
logger.warning("GhostContentApp: 'version' not found in integration credentials. Defaulting to 'v5.0'.")
|
|
@@ -10,10 +10,10 @@ class GithubApp(APIApplication):
|
|
|
10
10
|
self.base_api_url = "https://api.github.com/repos"
|
|
11
11
|
self.base_url = "https://api.github.com"
|
|
12
12
|
|
|
13
|
-
def
|
|
13
|
+
async def _aget_headers(self):
|
|
14
14
|
if not self.integration:
|
|
15
15
|
raise ValueError("Integration not configured")
|
|
16
|
-
credentials = self.integration.
|
|
16
|
+
credentials = await self.integration.get_credentials_async()
|
|
17
17
|
if "headers" in credentials:
|
|
18
18
|
return credentials["headers"]
|
|
19
19
|
return {"Authorization": f"Bearer {credentials['access_token']}", "Accept": "application/vnd.github.v3+json"}
|
|
@@ -9,8 +9,8 @@ class GongApp(APIApplication):
|
|
|
9
9
|
super().__init__(name="gong", integration=integration, **kwargs)
|
|
10
10
|
self.base_url = "https://api.gong.io"
|
|
11
11
|
|
|
12
|
-
def
|
|
13
|
-
credentials = self.integration.
|
|
12
|
+
async def _aget_headers(self) -> dict[str, str]:
|
|
13
|
+
credentials = await self.integration.get_credentials_async()
|
|
14
14
|
api_key = credentials.get("api_key")
|
|
15
15
|
secret = credentials.get("secret")
|
|
16
16
|
api_key_b64 = b64encode(f"{api_key}:{secret}".encode()).decode()
|
|
@@ -9,17 +9,18 @@ This is automatically generated from OpenAPI schema for the GoogleDocsApp API.
|
|
|
9
9
|
|
|
10
10
|
| Tool | Description |
|
|
11
11
|
|------|-------------|
|
|
12
|
-
| `create_document` | Creates a blank Google Document with a specified title
|
|
13
|
-
| `get_document` | Retrieves the complete
|
|
14
|
-
| `
|
|
15
|
-
| `
|
|
16
|
-
| `
|
|
17
|
-
| `
|
|
18
|
-
| `
|
|
19
|
-
| `
|
|
20
|
-
| `
|
|
21
|
-
| `
|
|
22
|
-
| `
|
|
23
|
-
| `
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
12
|
+
| `create_document` | Creates a blank Google Document with a specified title by sending a POST request to the Google Docs API. The function returns a dictionary containing the new document's metadata, including the unique document ID required by other functions for subsequent modifications or retrieval. Note that you need to call other google_docs functions (e.g. `google_docs__insert_text`) to actually add content after creating the document. |
|
|
13
|
+
| `get_document` | Retrieves the complete, raw JSON object for a Google Document by its ID. This function returns the full, unprocessed API response with all metadata and structural elements, distinguishing it from `get_document_content`, which parses this data to extract only the title and plain text. |
|
|
14
|
+
| `get_document_content` | Retrieves and converts a Google Docs document into Markdown-formatted content. |
|
|
15
|
+
| `insert_text` | Inserts a text string at a specified index within a Google Document using the batchUpdate API. Unlike functions that format existing text or delete content ranges, this method specifically adds new textual content to the document body. |
|
|
16
|
+
| `apply_text_style` | Applies character-level formatting (e.g., bold, italic, color, links) to a specified text range. This function modifies text attributes directly, distinguishing it from `update_paragraph_style` which handles block-level properties like alignment. |
|
|
17
|
+
| `update_paragraph_style` | Applies paragraph-level formatting like alignment, named styles (e.g., 'HEADING_1'), and text direction to a text range in a Google Doc. Distinct from `apply_text_style`, which handles character formatting, this method modifies properties for entire paragraphs using the batchUpdate API. |
|
|
18
|
+
| `delete_content_range` | Removes content from a specified index range in a Google Document via the batchUpdate API. Unlike functions that delete entire elements (e.g., `delete_header`), this provides granular control by targeting content based on its precise start and end location, optionally within a specific segment or tab. |
|
|
19
|
+
| `insert_table` | Inserts a table with specified rows and columns at a given index in a Google Document using the batchUpdate API. It can optionally place the table within specific document segments, such as headers or footers, handling structural additions rather than text or style modifications. |
|
|
20
|
+
| `create_footer` | Creates a footer of a specified type in a Google Document using the batch update API. This function, distinct from `create_header`, can optionally associate the new footer with a specific section break, enabling section-specific footers within the document. |
|
|
21
|
+
| `create_footnote` | Inserts a numbered footnote reference into a Google Document using the batchUpdate API. The footnote can be placed at a precise index or at the end of a document segment, distinct from the `create_footer` function which adds standard page footers. |
|
|
22
|
+
| `delete_footer` | Deletes a specific footer from a Google Document using its unique ID via a batchUpdate API request. This operation removes the entire footer object, optionally within a specific tab, distinguishing it from functions that delete headers (`delete_header`) or general content (`delete_content_range`). |
|
|
23
|
+
| `create_header` | Creates a header of a specified type in a Google Document using the batchUpdate API. This function can optionally associate the new header with a specific section break, distinguishing it from the `create_footer` method, which performs the equivalent action for footers. |
|
|
24
|
+
| `delete_header` | Deletes a specific header from a Google Document using its unique ID via a batchUpdate API request. This function, the counterpart to `create_header`, removes headers and can optionally target a header within a specific tab. It requires both the document and header IDs for the operation. |
|
|
25
|
+
| `apply_list_style` | Applies a predefined list style (bulleted or numbered) to paragraphs within a specified range using a chosen preset. Unlike `delete_paragraph_bullets`, which removes list formatting, this function creates it, distinguishing it from other text and paragraph styling methods in the class. |
|
|
26
|
+
| `delete_paragraph_bullets` | Removes bullet points or numbering from paragraphs within a specified index range in a Google Document. This reverts list formatting to normal text while preserving content, acting as the inverse operation to the `apply_list_style` function. |
|
|
@@ -33,7 +33,7 @@ class GoogleDocsApp(APIApplication):
|
|
|
33
33
|
payload["Note"] = "You must load and call other google docs content functions (like google_docs__insert_text)"
|
|
34
34
|
return payload
|
|
35
35
|
|
|
36
|
-
def get_document(self, document_id: str) -> dict[str, Any]:
|
|
36
|
+
async def get_document(self, document_id: str) -> dict[str, Any]:
|
|
37
37
|
"""
|
|
38
38
|
Retrieves the complete, raw JSON object for a Google Document by its ID. This function returns the full, unprocessed API response with all metadata and structural elements, distinguishing it from `get_document_content`, which parses this data to extract only the title and plain text.
|
|
39
39
|
|
|
@@ -51,7 +51,7 @@ class GoogleDocsApp(APIApplication):
|
|
|
51
51
|
retrieve, read, api, document, google-docs, important
|
|
52
52
|
"""
|
|
53
53
|
url = f"{self.base_api_url}/{document_id}"
|
|
54
|
-
response = self.
|
|
54
|
+
response = await self._aget(url)
|
|
55
55
|
return response.json()
|
|
56
56
|
|
|
57
57
|
async def get_document_content(self, document_id: str) -> dict[str, Any]:
|
|
@@ -85,7 +85,7 @@ class GoogleDocsApp(APIApplication):
|
|
|
85
85
|
"""
|
|
86
86
|
import re
|
|
87
87
|
|
|
88
|
-
response = self.get_document(document_id)
|
|
88
|
+
response = await self.get_document(document_id)
|
|
89
89
|
title = response.get("title", "")
|
|
90
90
|
body_content = response.get("body", {}).get("content", [])
|
|
91
91
|
inline_objects = response.get("inlineObjects", {})
|
|
@@ -714,9 +714,11 @@ class GoogleDocsApp(APIApplication):
|
|
|
714
714
|
def list_tools(self):
|
|
715
715
|
return [
|
|
716
716
|
self.create_document,
|
|
717
|
+
self.get_document,
|
|
717
718
|
self.get_document_content,
|
|
718
719
|
self.insert_text,
|
|
719
720
|
self.apply_text_style,
|
|
721
|
+
self.update_paragraph_style,
|
|
720
722
|
self.delete_content_range,
|
|
721
723
|
self.insert_table,
|
|
722
724
|
self.create_footer,
|
|
@@ -726,5 +728,4 @@ class GoogleDocsApp(APIApplication):
|
|
|
726
728
|
self.delete_header,
|
|
727
729
|
self.apply_list_style,
|
|
728
730
|
self.delete_paragraph_bullets,
|
|
729
|
-
self.update_paragraph_style,
|
|
730
731
|
]
|
|
@@ -16,11 +16,10 @@ class GoogleGeminiApp(APIApplication):
|
|
|
16
16
|
super().__init__(name="google_gemini", integration=integration, **kwargs)
|
|
17
17
|
self._genai_client = None
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
def genai_client(self) -> genai.Client:
|
|
19
|
+
async def get_genai_client(self) -> genai.Client:
|
|
21
20
|
if self._genai_client is not None:
|
|
22
21
|
return self._genai_client
|
|
23
|
-
credentials = self.integration.
|
|
22
|
+
credentials = await self.integration.get_credentials_async()
|
|
24
23
|
api_key = credentials.get("api_key") or credentials.get("API_KEY") or credentials.get("apiKey")
|
|
25
24
|
if not api_key:
|
|
26
25
|
raise ValueError("API key not found in integration credentials")
|
|
@@ -54,17 +53,15 @@ class GoogleGeminiApp(APIApplication):
|
|
|
54
53
|
Tags:
|
|
55
54
|
text, generate, llm, important
|
|
56
55
|
"""
|
|
57
|
-
|
|
56
|
+
client = await self.get_genai_client()
|
|
57
|
+
response = client.models.generate_content(contents=prompt, model=model)
|
|
58
58
|
return response.text
|
|
59
59
|
|
|
60
60
|
async def generate_image(
|
|
61
61
|
self,
|
|
62
62
|
prompt: Annotated[str, "The prompt to generate image from"],
|
|
63
63
|
images: Annotated[list[str], "The reference image URLs"] | None = None,
|
|
64
|
-
model: Literal[
|
|
65
|
-
"gemini-3-pro-image-preview",
|
|
66
|
-
"gemini-2.5-flash-image"
|
|
67
|
-
] = "gemini-2.5-flash-image",
|
|
64
|
+
model: Literal["gemini-3-pro-image-preview", "gemini-2.5-flash-image"] = "gemini-2.5-flash-image",
|
|
68
65
|
) -> dict:
|
|
69
66
|
"""
|
|
70
67
|
Generates an image based on a text prompt and an optional reference image using the Google Gemini model.
|
|
@@ -74,7 +71,7 @@ class GoogleGeminiApp(APIApplication):
|
|
|
74
71
|
Args:
|
|
75
72
|
prompt (str): The descriptive text prompt to guide the image generation. For example: "A futuristic city at sunset with flying cars."
|
|
76
73
|
images (list[str], optional): An optional list of URLs to reference images. These images will be used as a basis for the generation.
|
|
77
|
-
model (str, optional): The Gemini model to use for image generation. Defaults to "gemini-2.5-flash-image".
|
|
74
|
+
model (str, optional): The Gemini model to use for image generation. Defaults to "gemini-2.5-flash-image".
|
|
78
75
|
|
|
79
76
|
Returns:
|
|
80
77
|
dict: A dictionary containing:
|
|
@@ -92,6 +89,7 @@ class GoogleGeminiApp(APIApplication):
|
|
|
92
89
|
Tags:
|
|
93
90
|
image, generate, vision, important
|
|
94
91
|
"""
|
|
92
|
+
client = await self.get_genai_client()
|
|
95
93
|
contents = [prompt]
|
|
96
94
|
if images:
|
|
97
95
|
for image in images:
|
|
@@ -104,7 +102,7 @@ class GoogleGeminiApp(APIApplication):
|
|
|
104
102
|
else:
|
|
105
103
|
image = Image.open(image)
|
|
106
104
|
contents.append(image)
|
|
107
|
-
response =
|
|
105
|
+
response = client.models.generate_content(model=model, contents=contents)
|
|
108
106
|
candidate = response.candidates[0]
|
|
109
107
|
text = ""
|
|
110
108
|
for part in candidate.content.parts:
|
|
@@ -119,11 +117,8 @@ class GoogleGeminiApp(APIApplication):
|
|
|
119
117
|
async def generate_audio(
|
|
120
118
|
self,
|
|
121
119
|
prompt: Annotated[str, "The prompt to generate audio from"],
|
|
122
|
-
model: Literal[
|
|
123
|
-
|
|
124
|
-
"gemini-2.5-pro-preview-tts"
|
|
125
|
-
] = "gemini-2.5-flash-preview-tts",
|
|
126
|
-
) -> str:
|
|
120
|
+
model: Literal["gemini-2.5-flash-preview-tts", "gemini-2.5-pro-preview-tts"] = "gemini-2.5-flash-preview-tts",
|
|
121
|
+
) -> str:
|
|
127
122
|
"""Generates audio from a given text prompt using the Google Gemini model's Text-to-Speech (TTS) capabilities.
|
|
128
123
|
This tool is useful for converting text into spoken audio, which can be used for voiceovers, accessibility features, or interactive applications.
|
|
129
124
|
It returns a dictionary containing the generated audio data (base64 encoded), its MIME type, and a suggested file name.
|
|
@@ -145,6 +140,7 @@ class GoogleGeminiApp(APIApplication):
|
|
|
145
140
|
Tags:
|
|
146
141
|
audio, generate, tts, speech, important
|
|
147
142
|
"""
|
|
143
|
+
client = await self.get_genai_client()
|
|
148
144
|
|
|
149
145
|
def wave_file(filename, pcm, channels=1, rate=24000, sample_width=2):
|
|
150
146
|
with wave.open(filename, "wb") as wf:
|
|
@@ -153,7 +149,7 @@ class GoogleGeminiApp(APIApplication):
|
|
|
153
149
|
wf.setframerate(rate)
|
|
154
150
|
wf.writeframes(pcm)
|
|
155
151
|
|
|
156
|
-
response =
|
|
152
|
+
response = client.models.generate_content(
|
|
157
153
|
model=model,
|
|
158
154
|
contents=prompt,
|
|
159
155
|
config=types.GenerateContentConfig(
|
|
@@ -174,8 +170,56 @@ class GoogleGeminiApp(APIApplication):
|
|
|
174
170
|
audio_base64 = base64.b64encode(data).decode("utf-8")
|
|
175
171
|
return {"type": "audio", "data": audio_base64, "mime_type": "audio/wav", "file_name": file_name}
|
|
176
172
|
|
|
173
|
+
async def analyze_image(
|
|
174
|
+
self,
|
|
175
|
+
images: Annotated[list[str], "The reference image URLs"],
|
|
176
|
+
prompt: Annotated[str, "The prompt to describe or ask about the image"] = "Describe this image",
|
|
177
|
+
model: Literal["gemini-2.5-flash", "gemini-2.5-pro"] = "gemini-2.5-flash",
|
|
178
|
+
) -> str:
|
|
179
|
+
"""
|
|
180
|
+
Analyzes one or more images based on a text prompt using the Google Gemini model.
|
|
181
|
+
This tool is capable of describing images, answering questions about them, or performing visual reasoning.
|
|
182
|
+
It accepts image URLs and a text prompt, returning a natural language response.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
images (list[str]): A list of URLs for the images to be analyzed.
|
|
186
|
+
prompt (str, optional): The text prompt or question about the images. Defaults to "Describe this image".
|
|
187
|
+
model (str, optional): The Gemini model to use for analysis. Defaults to "gemini-2.5-flash".
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
str: The generated text response containing the analysis or description of the images.
|
|
191
|
+
|
|
192
|
+
Raises:
|
|
193
|
+
requests.exceptions.RequestException: If there's an issue fetching a remote image.
|
|
194
|
+
FileNotFoundError: If a local image path is invalid.
|
|
195
|
+
Exception: If the underlying Gemini API call fails.
|
|
196
|
+
|
|
197
|
+
Tags:
|
|
198
|
+
image, analyze, vision, describe, question, important
|
|
199
|
+
"""
|
|
200
|
+
client = await self.get_genai_client()
|
|
201
|
+
contents = [prompt]
|
|
202
|
+
if images:
|
|
203
|
+
for image in images:
|
|
204
|
+
if image.startswith(("http://", "https://")):
|
|
205
|
+
import requests
|
|
206
|
+
|
|
207
|
+
response = requests.get(image)
|
|
208
|
+
response.raise_for_status()
|
|
209
|
+
image = Image.open(io.BytesIO(response.content))
|
|
210
|
+
else:
|
|
211
|
+
image = Image.open(image)
|
|
212
|
+
contents.append(image)
|
|
213
|
+
response = client.models.generate_content(model=model, contents=contents)
|
|
214
|
+
return response.text
|
|
215
|
+
|
|
177
216
|
def list_tools(self):
|
|
178
|
-
return [
|
|
217
|
+
return [
|
|
218
|
+
self.generate_text,
|
|
219
|
+
self.generate_image,
|
|
220
|
+
self.analyze_image,
|
|
221
|
+
self.generate_audio,
|
|
222
|
+
]
|
|
179
223
|
|
|
180
224
|
|
|
181
225
|
async def test_google_gemini():
|
|
@@ -9,7 +9,7 @@ This is automatically generated from OpenAPI schema for the GoogleSheetApp API.
|
|
|
9
9
|
|
|
10
10
|
| Tool | Description |
|
|
11
11
|
|------|-------------|
|
|
12
|
-
| `create_spreadsheet` | Creates a new, blank Google Spreadsheet file with a specified title. This function generates a completely new document, unlike `add_sheet` which adds a worksheet (tab) to an existing spreadsheet. It returns the API response containing the new spreadsheet's metadata. |
|
|
12
|
+
| `create_spreadsheet` | Creates a new, blank Google Spreadsheet file with a specified title. This function generates a completely new document, unlike `add_sheet` which adds a worksheet (tab) to an existing spreadsheet. It returns the API response containing the new spreadsheet's metadata. Note that you need to call other google_sheet functions (e.g. `google_sheet__write_values_to_sheet`) to actually add content after creating the spreadsheet. |
|
|
13
13
|
| `get_spreadsheet_metadata` | Retrieves a spreadsheet's metadata and structural properties, such as sheet names, IDs, and named ranges, using its unique ID. This function intentionally excludes cell data, distinguishing it from `get_values` which fetches the actual content within cells. |
|
|
14
14
|
| `get_values` | Retrieves cell values from a single, specified A1 notation range. Unlike `batch_get_values_by_range` which fetches multiple ranges, this function is for a singular query and provides options to control the data's output format (e.g., rows vs. columns, formatted vs. raw values). |
|
|
15
15
|
| `batch_get_values_by_range` | Efficiently retrieves values from multiple predefined A1 notation ranges in a single API request. Unlike `get_values`, which fetches a single range, or `batch_get_values_by_data_filter`, which uses dynamic filtering criteria, this function operates on a simple list of range strings for bulk data retrieval. |
|
|
@@ -34,3 +34,4 @@ This is automatically generated from OpenAPI schema for the GoogleSheetApp API.
|
|
|
34
34
|
| `analyze_table_schema` | Infers a specified table's schema by analyzing a data sample. After locating the table by name (a value discovered via `discover_tables`), this function determines the most likely data type and properties for each column, providing a detailed structural breakdown of its content. |
|
|
35
35
|
| `set_basic_filter` | Sets or updates a basic filter on a specified range within a sheet, enabling data sorting and filtering. The filter's target range and optional sort specifications are defined in a dictionary argument. It is the counterpart to `clear_basic_filter`, which removes an existing filter. |
|
|
36
36
|
| `format_cells` | Applies comprehensive formatting to a specified cell range in a worksheet. It modifies visual properties like text style, color, alignment, borders, and can merge cells, without altering the underlying cell values, distinguishing it from data-modification functions like `update_values`. |
|
|
37
|
+
| `batch_update_values` | Updates multiple ranges of values in a spreadsheet in a single batch request. This method allows you to update several disjoint ranges or multiple sheets simultaneously, which is more efficient than making separate calls for each range. |
|
|
@@ -1455,6 +1455,60 @@ class GoogleSheetApp(APIApplication):
|
|
|
1455
1455
|
response = await self._apost(url, data=request_body)
|
|
1456
1456
|
return self._handle_response(response)
|
|
1457
1457
|
|
|
1458
|
+
async def batch_update_values(
|
|
1459
|
+
self,
|
|
1460
|
+
spreadsheetId: str,
|
|
1461
|
+
data: list[dict],
|
|
1462
|
+
value_input_option: str = "USER_ENTERED",
|
|
1463
|
+
include_values_in_response: bool = False,
|
|
1464
|
+
response_value_render_option: str | None = None,
|
|
1465
|
+
response_date_time_render_option: str | None = None,
|
|
1466
|
+
) -> dict[str, Any]:
|
|
1467
|
+
"""
|
|
1468
|
+
Updates multiple ranges of values in a spreadsheet in a single batch request. This method allows you to update several disjoint ranges or multiple sheets simultaneously, which is more efficient than making separate calls for each range.
|
|
1469
|
+
|
|
1470
|
+
Args:
|
|
1471
|
+
spreadsheetId: The unique identifier of the Google Spreadsheet to update. Example: "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms"
|
|
1472
|
+
data: A list of dictionaries, where each dictionary represents a range to update. Each dictionary must contain:
|
|
1473
|
+
- range: The A1 notation of the range to update (e.g., "Sheet1!A1:B2").
|
|
1474
|
+
- values: A 2D list of values to write into that range.
|
|
1475
|
+
Example: [{"range": "Sheet1!A1", "values": [["Header"]]}, {"range": "Sheet2!B2", "values": [[100]]}]
|
|
1476
|
+
value_input_option: How the input data should be interpreted. Options: "RAW" (as-is) or "USER_ENTERED" (parsed as if typed by user). Defaults to "USER_ENTERED".
|
|
1477
|
+
include_values_in_response: If True, the response will include the values of the cells that were updated. Defaults to False.
|
|
1478
|
+
response_value_render_option: Determines how values in the response should be rendered. Options: "FORMATTED_VALUE", "UNFORMATTED_VALUE", "FORMULA".
|
|
1479
|
+
response_date_time_render_option: Determines how dates/times in the response should be rendered. Options: "SERIAL_NUMBER", "FORMATTED_STRING".
|
|
1480
|
+
|
|
1481
|
+
Returns:
|
|
1482
|
+
A dictionary containing the Google Sheets API response with details of the batch update.
|
|
1483
|
+
|
|
1484
|
+
Raises:
|
|
1485
|
+
HTTPError: When the API request fails due to invalid parameters or insufficient permissions
|
|
1486
|
+
ValueError: When spreadsheetId is empty or data is empty
|
|
1487
|
+
|
|
1488
|
+
Tags:
|
|
1489
|
+
batch, update, values, spreadsheet, write, important
|
|
1490
|
+
"""
|
|
1491
|
+
if not spreadsheetId:
|
|
1492
|
+
raise ValueError("spreadsheetId cannot be empty")
|
|
1493
|
+
if not data:
|
|
1494
|
+
raise ValueError("data cannot be empty")
|
|
1495
|
+
|
|
1496
|
+
url = f"{self.base_url}/{spreadsheetId}/values:batchUpdate"
|
|
1497
|
+
|
|
1498
|
+
request_body = {
|
|
1499
|
+
"valueInputOption": value_input_option,
|
|
1500
|
+
"data": data,
|
|
1501
|
+
"includeValuesInResponse": include_values_in_response,
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
if response_value_render_option:
|
|
1505
|
+
request_body["responseValueRenderOption"] = response_value_render_option
|
|
1506
|
+
if response_date_time_render_option:
|
|
1507
|
+
request_body["responseDateTimeRenderOption"] = response_date_time_render_option
|
|
1508
|
+
|
|
1509
|
+
response = await self._apost(url, data=request_body)
|
|
1510
|
+
return self._handle_response(response)
|
|
1511
|
+
|
|
1458
1512
|
def list_tools(self):
|
|
1459
1513
|
"""Returns a list of methods exposed as tools."""
|
|
1460
1514
|
return [
|
|
@@ -1483,4 +1537,5 @@ class GoogleSheetApp(APIApplication):
|
|
|
1483
1537
|
self.analyze_table_schema,
|
|
1484
1538
|
self.set_basic_filter,
|
|
1485
1539
|
self.format_cells,
|
|
1540
|
+
self.batch_update_values,
|
|
1486
1541
|
]
|
|
@@ -9,36 +9,14 @@ This is automatically generated from OpenAPI schema for the HeygenApp API.
|
|
|
9
9
|
|
|
10
10
|
| Tool | Description |
|
|
11
11
|
|------|-------------|
|
|
12
|
-
| `get_v1_voice_list` | Retrieves the list of available voices from the v1 voice API endpoint. |
|
|
13
|
-
| `get_v1_avatar_list` | Retrieves a list of available avatars from the v1 API endpoint. |
|
|
14
|
-
| `get_v2_voices` | Retrieves the list of available v2 voices from the API endpoint. |
|
|
15
12
|
| `get_v2_avatars` | Retrieves a list of avatar objects from the /v2/avatars API endpoint. |
|
|
16
|
-
| `
|
|
17
|
-
| `
|
|
18
|
-
| `
|
|
19
|
-
| `
|
|
20
|
-
| `
|
|
21
|
-
| `
|
|
22
|
-
| `
|
|
23
|
-
| `
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
26
|
-
| `get_streaming_list` | Retrieves the list of available streaming resources from the remote API. |
|
|
27
|
-
| `post_streaming_ice` | Sends an ICE candidate for a streaming session to the server and returns the JSON response. |
|
|
28
|
-
| `post_streaming_task` | Submits a streaming task for the specified session and text input, returning the response from the remote API. |
|
|
29
|
-
| `post_streaming_stop` | Stops an ongoing streaming session by sending a stop request for the specified session ID. |
|
|
30
|
-
| `post_streaming_interrupt` | Sends a request to interrupt an active streaming session identified by the given session ID. |
|
|
31
|
-
| `post_streaming_create_token` | Creates a new streaming token with an optional expiry time by sending a POST request to the streaming token API endpoint. |
|
|
32
|
-
| `get_streaming_avatar_list` | Retrieves a list of available streaming avatars from the API endpoint. |
|
|
33
|
-
| `get_v1_webhook_list` | Retrieves a list of all registered webhooks via the v1 API endpoint. |
|
|
34
|
-
| `post_v1_webhook_endpoint_add` | Registers a new webhook endpoint with the specified URL and events. |
|
|
35
|
-
| `delete_v1_webhook_endpoint_by_id` | Deletes a webhook endpoint identified by its ID via a DELETE request to the v1 API. |
|
|
36
|
-
| `get_v1_webhook_endpoint_list` | Retrieves a list of webhook endpoints from the v1 API. |
|
|
37
|
-
| `get_v1_talking_photo_list` | Retrieves the list of talking photos from the v1 API endpoint. |
|
|
38
|
-
| `delete_v2_talking_photo_by_id` | Deletes a v2 talking photo resource identified by its unique ID. |
|
|
39
|
-
| `post_personalized_video_add_contact` | Adds a new contact to a personalized video project by sending the contact variables to the server. |
|
|
40
|
-
| `get_personalized_video_audience_detail` | Retrieves detailed information about a personalized video audience by ID. |
|
|
41
|
-
| `get_personalized_video_project_detail` | Retrieves the details of a personalized video project by its unique identifier. |
|
|
42
|
-
| `get_v2_user_remaining_quota` | Retrieves the current remaining quota information for the user from the v2 API endpoint. |
|
|
43
|
-
| `post_v1_asset_upload` | Uploads an asset to the server using a POST request to the '/v1/asset' endpoint. |
|
|
44
|
-
| `get_v1_video_status` | Retrieves the status of a video by making a GET request to the v1 video_status endpoint. |
|
|
13
|
+
| `list_avatar_groups` | Retrieves a list of avatar groups from the /v2/avatar_group.list API endpoint. |
|
|
14
|
+
| `list_avatars_in_group` | Retrieves a list of avatars from a specific avatar group. |
|
|
15
|
+
| `get_avatar_details` | Retrieves detailed information about a specific avatar by its ID. |
|
|
16
|
+
| `create_avatar_video` | Creates a new avatar video using the /v2/video/generate API endpoint. |
|
|
17
|
+
| `get_video_status` | Retrieves the status and details of a specific video by ID using the /v1/video_status.get endpoint. |
|
|
18
|
+
| `create_folder` | Creates a new folder under your account using the /v1/folders/create endpoint. |
|
|
19
|
+
| `list_folders` | Retrieves a paginated list of folders created under your account using the /v1/folders endpoint. |
|
|
20
|
+
| `update_folder` | Updates the name of an existing folder using the /v1/folders/{folder_id} endpoint. |
|
|
21
|
+
| `trash_folder` | Deletes (trashes) a specific folder by its unique folder ID using the /v1/folders/{folder_id}/trash endpoint. |
|
|
22
|
+
| `restore_folder` | Restores a previously deleted folder using the /v1/folders/{folder_id}/restore endpoint. |
|