universal-mcp-applications 0.1.1__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/ahrefs/README.md +51 -0
- universal_mcp/applications/ahrefs/__init__.py +1 -0
- universal_mcp/applications/ahrefs/app.py +2291 -0
- universal_mcp/applications/airtable/README.md +22 -0
- universal_mcp/applications/airtable/__init__.py +1 -0
- universal_mcp/applications/airtable/app.py +479 -0
- universal_mcp/applications/apollo/README.md +44 -0
- universal_mcp/applications/apollo/__init__.py +1 -0
- universal_mcp/applications/apollo/app.py +1847 -0
- universal_mcp/applications/asana/README.md +199 -0
- universal_mcp/applications/asana/__init__.py +1 -0
- universal_mcp/applications/asana/app.py +9509 -0
- universal_mcp/applications/aws-s3/README.md +0 -0
- universal_mcp/applications/aws-s3/__init__.py +1 -0
- universal_mcp/applications/aws-s3/app.py +552 -0
- universal_mcp/applications/bill/README.md +0 -0
- universal_mcp/applications/bill/__init__.py +1 -0
- universal_mcp/applications/bill/app.py +8705 -0
- universal_mcp/applications/box/README.md +307 -0
- universal_mcp/applications/box/__init__.py +1 -0
- universal_mcp/applications/box/app.py +15987 -0
- universal_mcp/applications/braze/README.md +106 -0
- universal_mcp/applications/braze/__init__.py +1 -0
- universal_mcp/applications/braze/app.py +4754 -0
- universal_mcp/applications/cal-com-v2/README.md +150 -0
- universal_mcp/applications/cal-com-v2/__init__.py +1 -0
- universal_mcp/applications/cal-com-v2/app.py +5541 -0
- universal_mcp/applications/calendly/README.md +53 -0
- universal_mcp/applications/calendly/__init__.py +1 -0
- universal_mcp/applications/calendly/app.py +1436 -0
- universal_mcp/applications/canva/README.md +43 -0
- universal_mcp/applications/canva/__init__.py +1 -0
- universal_mcp/applications/canva/app.py +941 -0
- universal_mcp/applications/clickup/README.md +135 -0
- universal_mcp/applications/clickup/__init__.py +1 -0
- universal_mcp/applications/clickup/app.py +5009 -0
- universal_mcp/applications/coda/README.md +108 -0
- universal_mcp/applications/coda/__init__.py +1 -0
- universal_mcp/applications/coda/app.py +3671 -0
- universal_mcp/applications/confluence/README.md +198 -0
- universal_mcp/applications/confluence/__init__.py +1 -0
- universal_mcp/applications/confluence/app.py +6273 -0
- universal_mcp/applications/contentful/README.md +17 -0
- universal_mcp/applications/contentful/__init__.py +1 -0
- universal_mcp/applications/contentful/app.py +364 -0
- universal_mcp/applications/crustdata/README.md +25 -0
- universal_mcp/applications/crustdata/__init__.py +1 -0
- universal_mcp/applications/crustdata/app.py +586 -0
- universal_mcp/applications/dialpad/README.md +202 -0
- universal_mcp/applications/dialpad/__init__.py +1 -0
- universal_mcp/applications/dialpad/app.py +5949 -0
- universal_mcp/applications/digitalocean/README.md +463 -0
- universal_mcp/applications/digitalocean/__init__.py +1 -0
- universal_mcp/applications/digitalocean/app.py +20835 -0
- universal_mcp/applications/domain-checker/README.md +13 -0
- universal_mcp/applications/domain-checker/__init__.py +1 -0
- universal_mcp/applications/domain-checker/app.py +265 -0
- universal_mcp/applications/e2b/README.md +12 -0
- universal_mcp/applications/e2b/__init__.py +1 -0
- universal_mcp/applications/e2b/app.py +187 -0
- universal_mcp/applications/elevenlabs/README.md +88 -0
- universal_mcp/applications/elevenlabs/__init__.py +1 -0
- universal_mcp/applications/elevenlabs/app.py +3235 -0
- universal_mcp/applications/exa/README.md +15 -0
- universal_mcp/applications/exa/__init__.py +1 -0
- universal_mcp/applications/exa/app.py +221 -0
- universal_mcp/applications/falai/README.md +17 -0
- universal_mcp/applications/falai/__init__.py +1 -0
- universal_mcp/applications/falai/app.py +331 -0
- universal_mcp/applications/figma/README.md +49 -0
- universal_mcp/applications/figma/__init__.py +1 -0
- universal_mcp/applications/figma/app.py +1090 -0
- universal_mcp/applications/firecrawl/README.md +20 -0
- universal_mcp/applications/firecrawl/__init__.py +1 -0
- universal_mcp/applications/firecrawl/app.py +514 -0
- universal_mcp/applications/fireflies/README.md +25 -0
- universal_mcp/applications/fireflies/__init__.py +1 -0
- universal_mcp/applications/fireflies/app.py +506 -0
- universal_mcp/applications/fpl/README.md +23 -0
- universal_mcp/applications/fpl/__init__.py +1 -0
- universal_mcp/applications/fpl/app.py +1327 -0
- universal_mcp/applications/fpl/utils/api.py +142 -0
- universal_mcp/applications/fpl/utils/fixtures.py +629 -0
- universal_mcp/applications/fpl/utils/helper.py +982 -0
- universal_mcp/applications/fpl/utils/league_utils.py +546 -0
- universal_mcp/applications/fpl/utils/position_utils.py +68 -0
- universal_mcp/applications/ghost-content/README.md +25 -0
- universal_mcp/applications/ghost-content/__init__.py +1 -0
- universal_mcp/applications/ghost-content/app.py +654 -0
- universal_mcp/applications/github/README.md +1049 -0
- universal_mcp/applications/github/__init__.py +1 -0
- universal_mcp/applications/github/app.py +50600 -0
- universal_mcp/applications/gong/README.md +63 -0
- universal_mcp/applications/gong/__init__.py +1 -0
- universal_mcp/applications/gong/app.py +2297 -0
- universal_mcp/applications/google-ads/README.md +0 -0
- universal_mcp/applications/google-ads/__init__.py +1 -0
- universal_mcp/applications/google-ads/app.py +23 -0
- universal_mcp/applications/google-calendar/README.md +21 -0
- universal_mcp/applications/google-calendar/__init__.py +1 -0
- universal_mcp/applications/google-calendar/app.py +574 -0
- universal_mcp/applications/google-docs/README.md +25 -0
- universal_mcp/applications/google-docs/__init__.py +1 -0
- universal_mcp/applications/google-docs/app.py +760 -0
- universal_mcp/applications/google-drive/README.md +68 -0
- universal_mcp/applications/google-drive/__init__.py +1 -0
- universal_mcp/applications/google-drive/app.py +4936 -0
- universal_mcp/applications/google-gemini/README.md +25 -0
- universal_mcp/applications/google-gemini/__init__.py +1 -0
- universal_mcp/applications/google-gemini/app.py +663 -0
- universal_mcp/applications/google-mail/README.md +31 -0
- universal_mcp/applications/google-mail/__init__.py +1 -0
- universal_mcp/applications/google-mail/app.py +1354 -0
- universal_mcp/applications/google-searchconsole/README.md +21 -0
- universal_mcp/applications/google-searchconsole/__init__.py +1 -0
- universal_mcp/applications/google-searchconsole/app.py +320 -0
- universal_mcp/applications/google-sheet/README.md +36 -0
- universal_mcp/applications/google-sheet/__init__.py +1 -0
- universal_mcp/applications/google-sheet/app.py +1941 -0
- universal_mcp/applications/hashnode/README.md +20 -0
- universal_mcp/applications/hashnode/__init__.py +1 -0
- universal_mcp/applications/hashnode/app.py +455 -0
- universal_mcp/applications/heygen/README.md +44 -0
- universal_mcp/applications/heygen/__init__.py +1 -0
- universal_mcp/applications/heygen/app.py +961 -0
- universal_mcp/applications/http-tools/README.md +16 -0
- universal_mcp/applications/http-tools/__init__.py +1 -0
- universal_mcp/applications/http-tools/app.py +153 -0
- universal_mcp/applications/hubspot/README.md +239 -0
- universal_mcp/applications/hubspot/__init__.py +1 -0
- universal_mcp/applications/hubspot/app.py +416 -0
- universal_mcp/applications/jira/README.md +600 -0
- universal_mcp/applications/jira/__init__.py +1 -0
- universal_mcp/applications/jira/app.py +28804 -0
- universal_mcp/applications/klaviyo/README.md +313 -0
- universal_mcp/applications/klaviyo/__init__.py +1 -0
- universal_mcp/applications/klaviyo/app.py +11236 -0
- universal_mcp/applications/linkedin/README.md +15 -0
- universal_mcp/applications/linkedin/__init__.py +1 -0
- universal_mcp/applications/linkedin/app.py +243 -0
- universal_mcp/applications/mailchimp/README.md +281 -0
- universal_mcp/applications/mailchimp/__init__.py +1 -0
- universal_mcp/applications/mailchimp/app.py +10937 -0
- universal_mcp/applications/markitdown/README.md +12 -0
- universal_mcp/applications/markitdown/__init__.py +1 -0
- universal_mcp/applications/markitdown/app.py +63 -0
- universal_mcp/applications/miro/README.md +151 -0
- universal_mcp/applications/miro/__init__.py +1 -0
- universal_mcp/applications/miro/app.py +5429 -0
- universal_mcp/applications/ms-teams/README.md +42 -0
- universal_mcp/applications/ms-teams/__init__.py +1 -0
- universal_mcp/applications/ms-teams/app.py +1823 -0
- universal_mcp/applications/neon/README.md +74 -0
- universal_mcp/applications/neon/__init__.py +1 -0
- universal_mcp/applications/neon/app.py +2018 -0
- universal_mcp/applications/notion/README.md +30 -0
- universal_mcp/applications/notion/__init__.py +1 -0
- universal_mcp/applications/notion/app.py +527 -0
- universal_mcp/applications/openai/README.md +22 -0
- universal_mcp/applications/openai/__init__.py +1 -0
- universal_mcp/applications/openai/app.py +759 -0
- universal_mcp/applications/outlook/README.md +20 -0
- universal_mcp/applications/outlook/__init__.py +1 -0
- universal_mcp/applications/outlook/app.py +444 -0
- universal_mcp/applications/perplexity/README.md +12 -0
- universal_mcp/applications/perplexity/__init__.py +1 -0
- universal_mcp/applications/perplexity/app.py +65 -0
- universal_mcp/applications/pipedrive/README.md +284 -0
- universal_mcp/applications/pipedrive/__init__.py +1 -0
- universal_mcp/applications/pipedrive/app.py +12924 -0
- universal_mcp/applications/posthog/README.md +132 -0
- universal_mcp/applications/posthog/__init__.py +1 -0
- universal_mcp/applications/posthog/app.py +7125 -0
- universal_mcp/applications/reddit/README.md +135 -0
- universal_mcp/applications/reddit/__init__.py +1 -0
- universal_mcp/applications/reddit/app.py +4652 -0
- universal_mcp/applications/replicate/README.md +18 -0
- universal_mcp/applications/replicate/__init__.py +1 -0
- universal_mcp/applications/replicate/app.py +495 -0
- universal_mcp/applications/resend/README.md +40 -0
- universal_mcp/applications/resend/__init__.py +1 -0
- universal_mcp/applications/resend/app.py +881 -0
- universal_mcp/applications/retell/README.md +21 -0
- universal_mcp/applications/retell/__init__.py +1 -0
- universal_mcp/applications/retell/app.py +333 -0
- universal_mcp/applications/rocketlane/README.md +70 -0
- universal_mcp/applications/rocketlane/__init__.py +1 -0
- universal_mcp/applications/rocketlane/app.py +4346 -0
- universal_mcp/applications/semanticscholar/README.md +25 -0
- universal_mcp/applications/semanticscholar/__init__.py +1 -0
- universal_mcp/applications/semanticscholar/app.py +482 -0
- universal_mcp/applications/semrush/README.md +44 -0
- universal_mcp/applications/semrush/__init__.py +1 -0
- universal_mcp/applications/semrush/app.py +2081 -0
- universal_mcp/applications/sendgrid/README.md +362 -0
- universal_mcp/applications/sendgrid/__init__.py +1 -0
- universal_mcp/applications/sendgrid/app.py +9752 -0
- universal_mcp/applications/sentry/README.md +186 -0
- universal_mcp/applications/sentry/__init__.py +1 -0
- universal_mcp/applications/sentry/app.py +7471 -0
- universal_mcp/applications/serpapi/README.md +14 -0
- universal_mcp/applications/serpapi/__init__.py +1 -0
- universal_mcp/applications/serpapi/app.py +293 -0
- universal_mcp/applications/sharepoint/README.md +0 -0
- universal_mcp/applications/sharepoint/__init__.py +1 -0
- universal_mcp/applications/sharepoint/app.py +215 -0
- universal_mcp/applications/shopify/README.md +321 -0
- universal_mcp/applications/shopify/__init__.py +1 -0
- universal_mcp/applications/shopify/app.py +15392 -0
- universal_mcp/applications/shortcut/README.md +128 -0
- universal_mcp/applications/shortcut/__init__.py +1 -0
- universal_mcp/applications/shortcut/app.py +4478 -0
- universal_mcp/applications/slack/README.md +0 -0
- universal_mcp/applications/slack/__init__.py +1 -0
- universal_mcp/applications/slack/app.py +570 -0
- universal_mcp/applications/spotify/README.md +91 -0
- universal_mcp/applications/spotify/__init__.py +1 -0
- universal_mcp/applications/spotify/app.py +2526 -0
- universal_mcp/applications/supabase/README.md +87 -0
- universal_mcp/applications/supabase/__init__.py +1 -0
- universal_mcp/applications/supabase/app.py +2970 -0
- universal_mcp/applications/tavily/README.md +12 -0
- universal_mcp/applications/tavily/__init__.py +1 -0
- universal_mcp/applications/tavily/app.py +51 -0
- universal_mcp/applications/trello/README.md +266 -0
- universal_mcp/applications/trello/__init__.py +1 -0
- universal_mcp/applications/trello/app.py +10875 -0
- universal_mcp/applications/twillo/README.md +0 -0
- universal_mcp/applications/twillo/__init__.py +1 -0
- universal_mcp/applications/twillo/app.py +269 -0
- universal_mcp/applications/twitter/README.md +100 -0
- universal_mcp/applications/twitter/__init__.py +1 -0
- universal_mcp/applications/twitter/api_segments/__init__.py +0 -0
- universal_mcp/applications/twitter/api_segments/api_segment_base.py +51 -0
- universal_mcp/applications/twitter/api_segments/compliance_api.py +122 -0
- universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +255 -0
- universal_mcp/applications/twitter/api_segments/dm_events_api.py +140 -0
- universal_mcp/applications/twitter/api_segments/likes_api.py +159 -0
- universal_mcp/applications/twitter/api_segments/lists_api.py +395 -0
- universal_mcp/applications/twitter/api_segments/openapi_json_api.py +34 -0
- universal_mcp/applications/twitter/api_segments/spaces_api.py +309 -0
- universal_mcp/applications/twitter/api_segments/trends_api.py +40 -0
- universal_mcp/applications/twitter/api_segments/tweets_api.py +1403 -0
- universal_mcp/applications/twitter/api_segments/usage_api.py +40 -0
- universal_mcp/applications/twitter/api_segments/users_api.py +1498 -0
- universal_mcp/applications/twitter/app.py +46 -0
- universal_mcp/applications/unipile/README.md +28 -0
- universal_mcp/applications/unipile/__init__.py +1 -0
- universal_mcp/applications/unipile/app.py +829 -0
- universal_mcp/applications/whatsapp/README.md +23 -0
- universal_mcp/applications/whatsapp/__init__.py +1 -0
- universal_mcp/applications/whatsapp/app.py +595 -0
- universal_mcp/applications/whatsapp-business/README.md +34 -0
- universal_mcp/applications/whatsapp-business/__init__.py +1 -0
- universal_mcp/applications/whatsapp-business/app.py +1065 -0
- universal_mcp/applications/wrike/README.md +46 -0
- universal_mcp/applications/wrike/__init__.py +1 -0
- universal_mcp/applications/wrike/app.py +1583 -0
- universal_mcp/applications/youtube/README.md +57 -0
- universal_mcp/applications/youtube/__init__.py +1 -0
- universal_mcp/applications/youtube/app.py +1696 -0
- universal_mcp/applications/zenquotes/README.md +12 -0
- universal_mcp/applications/zenquotes/__init__.py +1 -0
- universal_mcp/applications/zenquotes/app.py +31 -0
- universal_mcp_applications-0.1.1.dist-info/METADATA +172 -0
- universal_mcp_applications-0.1.1.dist-info/RECORD +268 -0
- universal_mcp_applications-0.1.1.dist-info/WHEEL +4 -0
- universal_mcp_applications-0.1.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Replicate MCP Server
|
|
2
|
+
|
|
3
|
+
An MCP Server for the Replicate API.
|
|
4
|
+
|
|
5
|
+
## 🛠️ Tool List
|
|
6
|
+
|
|
7
|
+
This is automatically generated from OpenAPI schema for the Replicate API.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
| Tool | Description |
|
|
11
|
+
|------|-------------|
|
|
12
|
+
| `chat_deepseek_ai_deepseek_r1` | Generates predictions for AI-powered conversations and tasks using the DeepSeek AI model, enabling applications such as chatbot interactions and text completion, by sending HTTP POST requests to the specified endpoint. |
|
|
13
|
+
| `chat_deepseek_ai_deepseek_r11` | Retrieves the details of a specific prediction identified by {prediction_id}. |
|
|
14
|
+
| `meta_meta_llama31405b_instruct` | Generates predictions using Meta's multilingual instruction-tuned Llama 3.1 405B model for dialogue and returns the result. |
|
|
15
|
+
| `meta_meta_llama370b_instruct` | Submits a prediction request to the Meta Llama 3 70B Instruct model and returns the generated result. |
|
|
16
|
+
| `meta_meta_llama38b_instruct` | Initiates a prediction using the Meta-Llama-3-8B-Instruct model and returns the generated text or code output. |
|
|
17
|
+
| `mistralai_mistral7b_v01` | Generates predictions using the Mistral-7B model and returns the output. |
|
|
18
|
+
| `black_forest_labs_flux_schnell` | Generates high-quality images from text descriptions using a 12 billion parameter rectified flow transformer model. |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .app import ReplicateApp
|
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
import collections.abc
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from loguru import logger
|
|
6
|
+
from universal_mcp.applications.application import APIApplication
|
|
7
|
+
from universal_mcp.exceptions import NotAuthorizedError, ToolError
|
|
8
|
+
from universal_mcp.integrations import Integration
|
|
9
|
+
|
|
10
|
+
import replicate
|
|
11
|
+
from replicate.exceptions import ModelError as ReplicateModelError
|
|
12
|
+
from replicate.exceptions import ReplicateError as ReplicateAPIError
|
|
13
|
+
from replicate.prediction import Prediction
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ReplicateApp(APIApplication):
|
|
17
|
+
"""
|
|
18
|
+
Application for interacting with the Replicate API.
|
|
19
|
+
|
|
20
|
+
Provides tools to run models, manage predictions (submit, get status, retrieve results, cancel),
|
|
21
|
+
upload files, and a specialized tool for generating images.
|
|
22
|
+
|
|
23
|
+
Authentication is handled by the configured Integration provided by the
|
|
24
|
+
Universal MCP server, fetching the necessary Replicate API token.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, integration: Integration, **kwargs) -> None:
|
|
28
|
+
super().__init__(name="replicate", integration=integration, **kwargs)
|
|
29
|
+
self._replicate_client: replicate.Client | None = None
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def replicate_client(self) -> replicate.Client:
|
|
33
|
+
if self._replicate_client is None:
|
|
34
|
+
credentials = self.integration.get_credentials()
|
|
35
|
+
logger.info(
|
|
36
|
+
f"ReplicateApp: Credentials from integration: {credentials}"
|
|
37
|
+
) # Be careful logging credentials
|
|
38
|
+
api_key = (
|
|
39
|
+
credentials.get("api_key")
|
|
40
|
+
or credentials.get("API_KEY")
|
|
41
|
+
or credentials.get("apiKey")
|
|
42
|
+
)
|
|
43
|
+
if not api_key:
|
|
44
|
+
logger.error(
|
|
45
|
+
f"Integration {type(self.integration).__name__} returned credentials for Replicate in unexpected format or key is missing."
|
|
46
|
+
)
|
|
47
|
+
raise NotAuthorizedError(
|
|
48
|
+
"Integration returned empty or invalid API key/token for Replicate."
|
|
49
|
+
)
|
|
50
|
+
self._replicate_client = replicate.Client(api_token=api_key)
|
|
51
|
+
return self._replicate_client
|
|
52
|
+
|
|
53
|
+
async def run(
|
|
54
|
+
self,
|
|
55
|
+
model_ref: str,
|
|
56
|
+
inputs: dict[str, Any],
|
|
57
|
+
use_file_output: bool | None = True,
|
|
58
|
+
) -> Any:
|
|
59
|
+
"""
|
|
60
|
+
Run a Replicate model and wait for its output. This is a blocking call from the user's perspective.
|
|
61
|
+
If the model output is an iterator, this tool will collect all items into a list.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
model_ref: The model identifier string (e.g., "owner/name" or "owner/name:version_id").
|
|
65
|
+
inputs: A dictionary of inputs for the model.
|
|
66
|
+
use_file_output: If True (default), file URLs in output are wrapped in FileOutput objects.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
The model's output. If the model streams output, a list of all streamed items is returned.
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
ToolError: If the Replicate API request fails or the model encounters an error.
|
|
73
|
+
|
|
74
|
+
Tags:
|
|
75
|
+
run, execute, ai, synchronous, replicate, important
|
|
76
|
+
"""
|
|
77
|
+
try:
|
|
78
|
+
logger.info(
|
|
79
|
+
f"Running Replicate model {model_ref} with inputs: {list(inputs.keys())}"
|
|
80
|
+
)
|
|
81
|
+
# Use async_run which handles polling and waiting
|
|
82
|
+
result = await self.replicate_client.async_run(
|
|
83
|
+
ref=model_ref,
|
|
84
|
+
input=inputs,
|
|
85
|
+
use_file_output=use_file_output,
|
|
86
|
+
)
|
|
87
|
+
if isinstance(result, collections.abc.AsyncIterator):
|
|
88
|
+
logger.info(
|
|
89
|
+
f"Model {model_ref} returned an async iterator, collecting results."
|
|
90
|
+
)
|
|
91
|
+
collected_result = [item async for item in result]
|
|
92
|
+
logger.info(
|
|
93
|
+
f"Collected {len(collected_result)} items from iterator for {model_ref}."
|
|
94
|
+
)
|
|
95
|
+
return collected_result
|
|
96
|
+
logger.info(f"Model {model_ref} run completed successfully.")
|
|
97
|
+
return result
|
|
98
|
+
except ReplicateModelError as e:
|
|
99
|
+
logger.error(
|
|
100
|
+
f"Model error running Replicate model {model_ref}: {e.prediction.error}",
|
|
101
|
+
exc_info=True,
|
|
102
|
+
)
|
|
103
|
+
raise ToolError(
|
|
104
|
+
f"Model {model_ref} failed with error: {e.prediction.error}"
|
|
105
|
+
) from e
|
|
106
|
+
except ReplicateAPIError as e:
|
|
107
|
+
logger.error(
|
|
108
|
+
f"API error running Replicate model {model_ref}: {e.detail}",
|
|
109
|
+
exc_info=True,
|
|
110
|
+
)
|
|
111
|
+
raise ToolError(
|
|
112
|
+
f"Failed to run Replicate model {model_ref}: {e.detail}"
|
|
113
|
+
) from e
|
|
114
|
+
except Exception as e:
|
|
115
|
+
logger.error(
|
|
116
|
+
f"Unexpected error running Replicate model {model_ref}: {e}",
|
|
117
|
+
exc_info=True,
|
|
118
|
+
)
|
|
119
|
+
raise ToolError(
|
|
120
|
+
f"An unexpected error occurred while running model {model_ref}: {e}"
|
|
121
|
+
) from e
|
|
122
|
+
|
|
123
|
+
async def submit_prediction(
|
|
124
|
+
self,
|
|
125
|
+
model_ref: str,
|
|
126
|
+
inputs: dict[str, Any],
|
|
127
|
+
webhook: str | None = None,
|
|
128
|
+
webhook_events_filter: list[str] | None = None,
|
|
129
|
+
) -> str:
|
|
130
|
+
"""
|
|
131
|
+
Submits a prediction request to Replicate for asynchronous processing.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
model_ref: The model identifier string (e.g., "owner/name" or "owner/name:version_id").
|
|
135
|
+
inputs: A dictionary of inputs for the model.
|
|
136
|
+
webhook: URL to receive a POST request with prediction updates.
|
|
137
|
+
webhook_events_filter: List of events to trigger webhooks (e.g., ["start", "output", "logs", "completed"]).
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
The ID (str) of the created prediction.
|
|
141
|
+
|
|
142
|
+
Raises:
|
|
143
|
+
ToolError: If the Replicate API request fails.
|
|
144
|
+
|
|
145
|
+
Tags:
|
|
146
|
+
submit, async_job, start, ai, queue, replicate
|
|
147
|
+
"""
|
|
148
|
+
try:
|
|
149
|
+
logger.info(
|
|
150
|
+
f"Submitting prediction for Replicate model {model_ref} with inputs: {list(inputs.keys())}"
|
|
151
|
+
)
|
|
152
|
+
# Ensure wait is False for async submission behavior
|
|
153
|
+
# The version parameter in predictions.create can be a model_ref string
|
|
154
|
+
prediction_params = {}
|
|
155
|
+
if webhook:
|
|
156
|
+
prediction_params["webhook"] = webhook
|
|
157
|
+
if webhook_events_filter:
|
|
158
|
+
prediction_params["webhook_events_filter"] = webhook_events_filter
|
|
159
|
+
|
|
160
|
+
prediction = await self.replicate_client.predictions.async_create(
|
|
161
|
+
version=model_ref, # 'version' here means the model/version ref string
|
|
162
|
+
input=inputs,
|
|
163
|
+
wait=False, # Explicitly set wait to False for non-blocking submission
|
|
164
|
+
**prediction_params,
|
|
165
|
+
)
|
|
166
|
+
logger.info(f"Submitted prediction for {model_ref}, ID: {prediction.id}")
|
|
167
|
+
return prediction.id
|
|
168
|
+
except ReplicateAPIError as e:
|
|
169
|
+
logger.error(
|
|
170
|
+
f"API error submitting prediction for Replicate model {model_ref}: {e.detail}",
|
|
171
|
+
exc_info=True,
|
|
172
|
+
)
|
|
173
|
+
raise ToolError(
|
|
174
|
+
f"Failed to submit prediction for Replicate model {model_ref}: {e.detail}"
|
|
175
|
+
) from e
|
|
176
|
+
except Exception as e:
|
|
177
|
+
logger.error(
|
|
178
|
+
f"Unexpected error submitting prediction for Replicate model {model_ref}: {e}",
|
|
179
|
+
exc_info=True,
|
|
180
|
+
)
|
|
181
|
+
raise ToolError(
|
|
182
|
+
f"An unexpected error occurred while submitting prediction for {model_ref}: {e}"
|
|
183
|
+
) from e
|
|
184
|
+
|
|
185
|
+
async def get_prediction(self, prediction_id: str) -> Prediction:
|
|
186
|
+
"""
|
|
187
|
+
Retrieves the current state and details of a Replicate prediction.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
prediction_id: The unique ID of the prediction.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
A Replicate Prediction object containing status, logs, output (if ready), etc.
|
|
194
|
+
|
|
195
|
+
Raises:
|
|
196
|
+
ToolError: If the Replicate API request fails.
|
|
197
|
+
|
|
198
|
+
Tags:
|
|
199
|
+
status, check, async_job, monitoring, ai, replicate
|
|
200
|
+
"""
|
|
201
|
+
try:
|
|
202
|
+
logger.info(f"Getting status for Replicate prediction ID: {prediction_id}")
|
|
203
|
+
prediction = await self.replicate_client.predictions.async_get(
|
|
204
|
+
id=prediction_id
|
|
205
|
+
)
|
|
206
|
+
logger.info(f"Status for prediction {prediction_id}: {prediction.status}")
|
|
207
|
+
return prediction
|
|
208
|
+
except ReplicateAPIError as e:
|
|
209
|
+
logger.error(
|
|
210
|
+
f"API error getting status for Replicate prediction {prediction_id}: {e.detail}",
|
|
211
|
+
exc_info=True,
|
|
212
|
+
)
|
|
213
|
+
raise ToolError(
|
|
214
|
+
f"Failed to get status for Replicate prediction {prediction_id}: {e.detail}"
|
|
215
|
+
) from e
|
|
216
|
+
except Exception as e:
|
|
217
|
+
logger.error(
|
|
218
|
+
f"Unexpected error getting status for Replicate prediction {prediction_id}: {e}",
|
|
219
|
+
exc_info=True,
|
|
220
|
+
)
|
|
221
|
+
raise ToolError(
|
|
222
|
+
f"An unexpected error occurred while getting status for prediction {prediction_id}: {e}"
|
|
223
|
+
) from e
|
|
224
|
+
|
|
225
|
+
async def fetch_prediction_output(self, prediction_id: str) -> Any:
|
|
226
|
+
"""
|
|
227
|
+
Retrieves the output of a completed Replicate prediction.
|
|
228
|
+
If the prediction is not yet complete, this method will wait for it to finish.
|
|
229
|
+
If the model output is an iterator, this tool will collect all items into a list.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
prediction_id: The unique ID of the prediction.
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
The output of the prediction. If the model streams output, a list of all streamed items is returned.
|
|
236
|
+
|
|
237
|
+
Raises:
|
|
238
|
+
ToolError: If the prediction fails, is canceled, or an API error occurs.
|
|
239
|
+
|
|
240
|
+
Tags:
|
|
241
|
+
result, fetch_output, async_job, wait, ai, replicate
|
|
242
|
+
"""
|
|
243
|
+
try:
|
|
244
|
+
logger.info(f"Fetching output for Replicate prediction ID: {prediction_id}")
|
|
245
|
+
prediction = await self.replicate_client.predictions.async_get(
|
|
246
|
+
id=prediction_id
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
if prediction.status not in ["succeeded", "failed", "canceled"]:
|
|
250
|
+
logger.info(
|
|
251
|
+
f"Prediction {prediction_id} status is {prediction.status}. Waiting for completion..."
|
|
252
|
+
)
|
|
253
|
+
await (
|
|
254
|
+
prediction.async_wait()
|
|
255
|
+
) # This updates the prediction object in-place
|
|
256
|
+
logger.info(
|
|
257
|
+
f"Prediction {prediction_id} finished with status: {prediction.status}"
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
if prediction.status == "failed":
|
|
261
|
+
logger.error(f"Prediction {prediction_id} failed: {prediction.error}")
|
|
262
|
+
raise ToolError(
|
|
263
|
+
f"Prediction {prediction_id} failed: {prediction.error}"
|
|
264
|
+
)
|
|
265
|
+
if prediction.status == "canceled":
|
|
266
|
+
logger.warning(f"Prediction {prediction_id} was canceled.")
|
|
267
|
+
raise ToolError(f"Prediction {prediction_id} was canceled.")
|
|
268
|
+
if prediction.status != "succeeded":
|
|
269
|
+
logger.error(
|
|
270
|
+
f"Prediction {prediction_id} did not succeed. Status: {prediction.status}"
|
|
271
|
+
)
|
|
272
|
+
raise ToolError(
|
|
273
|
+
f"Prediction {prediction_id} did not succeed. Status: {prediction.status}"
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
output = prediction.output
|
|
277
|
+
logger.info(f"Successfully fetched output for prediction {prediction_id}.")
|
|
278
|
+
return output
|
|
279
|
+
except (
|
|
280
|
+
ReplicateModelError
|
|
281
|
+
) as e: # Should be caught by prediction.status == "failed" mostly
|
|
282
|
+
logger.error(
|
|
283
|
+
f"Model error fetching output for Replicate prediction {prediction_id}: {e.prediction.error}",
|
|
284
|
+
exc_info=True,
|
|
285
|
+
)
|
|
286
|
+
raise ToolError(
|
|
287
|
+
f"Prediction {prediction_id} (model) failed: {e.prediction.error}"
|
|
288
|
+
) from e
|
|
289
|
+
except ReplicateAPIError as e:
|
|
290
|
+
logger.error(
|
|
291
|
+
f"API error fetching output for Replicate prediction {prediction_id}: {e.detail}",
|
|
292
|
+
exc_info=True,
|
|
293
|
+
)
|
|
294
|
+
raise ToolError(
|
|
295
|
+
f"Failed to fetch output for Replicate prediction {prediction_id}: {e.detail}"
|
|
296
|
+
) from e
|
|
297
|
+
except Exception as e:
|
|
298
|
+
logger.error(
|
|
299
|
+
f"Unexpected error fetching output for Replicate prediction {prediction_id}: {e}",
|
|
300
|
+
exc_info=True,
|
|
301
|
+
)
|
|
302
|
+
raise ToolError(
|
|
303
|
+
f"An unexpected error occurred while fetching output for prediction {prediction_id}: {e}"
|
|
304
|
+
) from e
|
|
305
|
+
|
|
306
|
+
async def cancel_prediction(self, prediction_id: str) -> None:
|
|
307
|
+
"""
|
|
308
|
+
Cancels a running or queued Replicate prediction.
|
|
309
|
+
|
|
310
|
+
Args:
|
|
311
|
+
prediction_id: The unique ID of the prediction to cancel.
|
|
312
|
+
|
|
313
|
+
Returns:
|
|
314
|
+
None.
|
|
315
|
+
|
|
316
|
+
Raises:
|
|
317
|
+
ToolError: If the cancellation request fails.
|
|
318
|
+
|
|
319
|
+
Tags:
|
|
320
|
+
cancel, async_job, ai, replicate, management
|
|
321
|
+
"""
|
|
322
|
+
try:
|
|
323
|
+
logger.info(f"Cancelling Replicate prediction ID: {prediction_id}")
|
|
324
|
+
prediction = await self.replicate_client.predictions.async_get(
|
|
325
|
+
id=prediction_id
|
|
326
|
+
)
|
|
327
|
+
if prediction.status not in ["succeeded", "failed", "canceled"]:
|
|
328
|
+
await prediction.async_cancel()
|
|
329
|
+
logger.info(
|
|
330
|
+
f"Cancel request sent for prediction {prediction_id}. New status: {prediction.status}"
|
|
331
|
+
)
|
|
332
|
+
else:
|
|
333
|
+
logger.warning(
|
|
334
|
+
f"Prediction {prediction_id} is already in a terminal state: {prediction.status}. Cannot cancel."
|
|
335
|
+
)
|
|
336
|
+
return None
|
|
337
|
+
except ReplicateAPIError as e:
|
|
338
|
+
logger.error(
|
|
339
|
+
f"API error cancelling Replicate prediction {prediction_id}: {e.detail}",
|
|
340
|
+
exc_info=True,
|
|
341
|
+
)
|
|
342
|
+
raise ToolError(
|
|
343
|
+
f"Failed to cancel Replicate prediction {prediction_id}: {e.detail}"
|
|
344
|
+
) from e
|
|
345
|
+
except Exception as e:
|
|
346
|
+
logger.error(
|
|
347
|
+
f"Unexpected error cancelling Replicate prediction {prediction_id}: {e}",
|
|
348
|
+
exc_info=True,
|
|
349
|
+
)
|
|
350
|
+
raise ToolError(
|
|
351
|
+
f"An unexpected error occurred while cancelling prediction {prediction_id}: {e}"
|
|
352
|
+
) from e
|
|
353
|
+
|
|
354
|
+
async def upload_file(self, file_path: str) -> str:
|
|
355
|
+
"""
|
|
356
|
+
Uploads a local file to Replicate and returns its public URL.
|
|
357
|
+
Replicate uses these URLs for file inputs in models.
|
|
358
|
+
|
|
359
|
+
Args:
|
|
360
|
+
file_path: The absolute or relative path to the local file.
|
|
361
|
+
|
|
362
|
+
Returns:
|
|
363
|
+
A string containing the public URL of the uploaded file (e.g., "https://replicate.delivery/pbxt/...").
|
|
364
|
+
|
|
365
|
+
Raises:
|
|
366
|
+
ToolError: If the file is not found or if the upload operation fails.
|
|
367
|
+
|
|
368
|
+
Tags:
|
|
369
|
+
upload, file, storage, replicate, important
|
|
370
|
+
"""
|
|
371
|
+
try:
|
|
372
|
+
path_obj = Path(file_path)
|
|
373
|
+
if not path_obj.exists():
|
|
374
|
+
raise FileNotFoundError(f"File not found at path: {file_path}")
|
|
375
|
+
|
|
376
|
+
logger.info(f"Uploading file to Replicate: {file_path}")
|
|
377
|
+
# The `async_create` method in `replicate.file.Files` handles opening the file.
|
|
378
|
+
uploaded_file_obj = await self.replicate_client.files.async_create(
|
|
379
|
+
file=path_obj
|
|
380
|
+
)
|
|
381
|
+
file_url = uploaded_file_obj.urls["get"]
|
|
382
|
+
logger.info(
|
|
383
|
+
f"File {file_path} uploaded successfully to Replicate. URL: {file_url}"
|
|
384
|
+
)
|
|
385
|
+
return file_url
|
|
386
|
+
except FileNotFoundError as e:
|
|
387
|
+
logger.error(
|
|
388
|
+
f"File not found for Replicate upload: {file_path}", exc_info=True
|
|
389
|
+
)
|
|
390
|
+
raise ToolError(f"File not found for Replicate upload: {file_path}") from e
|
|
391
|
+
except ReplicateAPIError as e:
|
|
392
|
+
logger.error(
|
|
393
|
+
f"API error uploading file {file_path} to Replicate: {e.detail}",
|
|
394
|
+
exc_info=True,
|
|
395
|
+
)
|
|
396
|
+
raise ToolError(
|
|
397
|
+
f"Failed to upload file {file_path} to Replicate: {e.detail}"
|
|
398
|
+
) from e
|
|
399
|
+
except Exception as e:
|
|
400
|
+
logger.error(
|
|
401
|
+
f"Unexpected error uploading file {file_path} to Replicate: {e}",
|
|
402
|
+
exc_info=True,
|
|
403
|
+
)
|
|
404
|
+
raise ToolError(
|
|
405
|
+
f"An unexpected error occurred while uploading file {file_path} to Replicate: {e}"
|
|
406
|
+
) from e
|
|
407
|
+
|
|
408
|
+
async def generate_image(
|
|
409
|
+
self,
|
|
410
|
+
prompt: str,
|
|
411
|
+
model_ref: str = "stability-ai/sdxl:c221b2b8ef527988fb59bf24a8b97c4561f1c671f73bd389f866bfb27c061316",
|
|
412
|
+
negative_prompt: str | None = None,
|
|
413
|
+
width: int | None = 1024,
|
|
414
|
+
height: int | None = 1024,
|
|
415
|
+
num_outputs: int | None = 1,
|
|
416
|
+
seed: int | None = None,
|
|
417
|
+
extra_arguments: dict[str, Any] | None = None,
|
|
418
|
+
) -> Any:
|
|
419
|
+
"""
|
|
420
|
+
Generates images using a specified Replicate model (defaults to SDXL).
|
|
421
|
+
This is a convenience wrapper around the `run` tool.
|
|
422
|
+
|
|
423
|
+
Args:
|
|
424
|
+
prompt: The text prompt for image generation.
|
|
425
|
+
model_ref: The Replicate model identifier string.
|
|
426
|
+
Defaults to "stability-ai/sdxl:7762fd07cf82c948538e41f63f77d685e02b063e37e496e96eefd46c929f9bdc".
|
|
427
|
+
negative_prompt: Optional text to specify what not to include.
|
|
428
|
+
width: Width of the generated image(s).
|
|
429
|
+
height: Height of the generated image(s).
|
|
430
|
+
num_outputs: Number of images to generate.
|
|
431
|
+
seed: Optional random seed for reproducibility.
|
|
432
|
+
extra_arguments: Dictionary of additional arguments specific to the model.
|
|
433
|
+
|
|
434
|
+
Returns:
|
|
435
|
+
The output from the image generation model, typically a list of image URLs.
|
|
436
|
+
|
|
437
|
+
Raises:
|
|
438
|
+
ToolError: If the image generation fails.
|
|
439
|
+
|
|
440
|
+
Tags:
|
|
441
|
+
generate, image, ai, replicate, sdxl, important, default
|
|
442
|
+
"""
|
|
443
|
+
logger.info(
|
|
444
|
+
f"Generating image with Replicate model {model_ref} for prompt: '{prompt[:30]}...'"
|
|
445
|
+
)
|
|
446
|
+
inputs = {
|
|
447
|
+
"prompt": prompt,
|
|
448
|
+
}
|
|
449
|
+
if negative_prompt is not None:
|
|
450
|
+
inputs["negative_prompt"] = negative_prompt
|
|
451
|
+
if width is not None:
|
|
452
|
+
inputs["width"] = width
|
|
453
|
+
if height is not None:
|
|
454
|
+
inputs["height"] = height
|
|
455
|
+
if num_outputs is not None:
|
|
456
|
+
inputs["num_outputs"] = num_outputs
|
|
457
|
+
if seed is not None:
|
|
458
|
+
inputs["seed"] = seed
|
|
459
|
+
|
|
460
|
+
if extra_arguments:
|
|
461
|
+
inputs.update(extra_arguments)
|
|
462
|
+
logger.debug(
|
|
463
|
+
f"Merged extra_arguments for image generation. Final input keys: {list(inputs.keys())}"
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
try:
|
|
467
|
+
# Use the run method which handles waiting and iterator collection
|
|
468
|
+
result = await self.run(model_ref=model_ref, inputs=inputs)
|
|
469
|
+
logger.info(f"Image generation successful for model {model_ref}.")
|
|
470
|
+
for index, item in enumerate(result):
|
|
471
|
+
with open(f"output_{index}.png", "wb") as file:
|
|
472
|
+
file.write(item.read())
|
|
473
|
+
return result
|
|
474
|
+
except Exception as e: # run method already wraps in ToolError
|
|
475
|
+
logger.error(
|
|
476
|
+
f"Error during generate_image call for model {model_ref}: {e}",
|
|
477
|
+
exc_info=True,
|
|
478
|
+
)
|
|
479
|
+
# Re-raise if it's already ToolError, or wrap if it's something unexpected from this level
|
|
480
|
+
if isinstance(e, ToolError):
|
|
481
|
+
raise
|
|
482
|
+
raise ToolError(
|
|
483
|
+
f"Image generation failed for model {model_ref}: {e}"
|
|
484
|
+
) from e
|
|
485
|
+
|
|
486
|
+
def list_tools(self) -> list[callable]:
|
|
487
|
+
return [
|
|
488
|
+
self.run,
|
|
489
|
+
self.submit_prediction,
|
|
490
|
+
self.get_prediction,
|
|
491
|
+
self.fetch_prediction_output,
|
|
492
|
+
self.cancel_prediction,
|
|
493
|
+
self.upload_file,
|
|
494
|
+
self.generate_image,
|
|
495
|
+
]
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# ResendApp MCP Server
|
|
2
|
+
|
|
3
|
+
An MCP Server for the ResendApp API.
|
|
4
|
+
|
|
5
|
+
## 🛠️ Tool List
|
|
6
|
+
|
|
7
|
+
This is automatically generated from OpenAPI schema for the ResendApp API.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
| Tool | Description |
|
|
11
|
+
|------|-------------|
|
|
12
|
+
| `send_email` | Sends an email to specified recipients using the Resend API. |
|
|
13
|
+
| `send_batch_emails` | Sends a batch of emails using the Resend API. |
|
|
14
|
+
| `get_email` | Retrieves a single email by its specified ID. |
|
|
15
|
+
| `update_scheduled_email` | Updates the scheduling of an email to a new time. |
|
|
16
|
+
| `cancel_scheduled_email` | Cancels a scheduled email using the provided email ID. |
|
|
17
|
+
| `create_domain` | Creates a new domain with the specified name. |
|
|
18
|
+
| `get_domain` | Retrieves a single domain by its ID. |
|
|
19
|
+
| `verify_domain` | Verifies an existing domain using the provided domain ID. |
|
|
20
|
+
| `update_domain` | Updates an existing domain's settings regarding open tracking, click tracking, and TLS enforcement. |
|
|
21
|
+
| `list_domains` | Retrieves a list of all domains for the authenticated user. |
|
|
22
|
+
| `remove_domain` | Removes an existing domain by its ID using the Resend API. |
|
|
23
|
+
| `create_api_key` | Creates a new API key for authenticating with Resend. |
|
|
24
|
+
| `list_api_keys` | Retrieves a list of all API keys available through the resend service. |
|
|
25
|
+
| `remove_api_key` | Removes an existing API key using the specified key ID. |
|
|
26
|
+
| `create_broadcast` | Creates a new broadcast to send to a specified audience. |
|
|
27
|
+
| `get_broadcast` | Retrieves a single broadcast by its ID. |
|
|
28
|
+
| `update_broadcast` | Updates a broadcast by modifying its HTML content and/or subject line. |
|
|
29
|
+
| `send_broadcast` | Starts sending a broadcast via the API. |
|
|
30
|
+
| `remove_broadcast` | Removes an existing broadcast with 'draft' status. |
|
|
31
|
+
| `list_broadcasts` | Retrieves a list of all available broadcasts using the configured API key. |
|
|
32
|
+
| `create_audience` | Creates a new audience (a list of contacts) with the specified name. |
|
|
33
|
+
| `get_audience` | Retrieves a single audience object from the API using the specified audience ID. |
|
|
34
|
+
| `remove_audience` | Removes an existing audience using the provided audience ID and returns the API response. |
|
|
35
|
+
| `list_audiences` | Retrieves a list of all audiences. |
|
|
36
|
+
| `create_contact` | Creates a contact within a specific audience. |
|
|
37
|
+
| `get_contact` | Retrieves a single contact from an audience by providing either a unique contact ID or an email address, ensuring exactly one identifier is given. |
|
|
38
|
+
| `update_contact` | Updates an existing contact, identified by ID or email, within a specified audience. |
|
|
39
|
+
| `remove_contact` | Removes a contact from an audience, identified by ID or email. |
|
|
40
|
+
| `list_contacts` | Lists all contacts from a specified audience. |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .app import ResendApp
|