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,20 @@
|
|
|
1
|
+
# FirecrawlApp MCP Server
|
|
2
|
+
|
|
3
|
+
An MCP Server for the FirecrawlApp API.
|
|
4
|
+
|
|
5
|
+
## 🛠️ Tool List
|
|
6
|
+
|
|
7
|
+
This is automatically generated from OpenAPI schema for the FirecrawlApp API.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
| Tool | Description |
|
|
11
|
+
|------|-------------|
|
|
12
|
+
| `scrape_url` | Scrapes a single URL using Firecrawl and returns the extracted data. |
|
|
13
|
+
| `search` | Performs a web search using Firecrawl's search capability. |
|
|
14
|
+
| `start_crawl` | Starts a async crawl job for a given URL using Firecrawl. Returns the job ID immediately. |
|
|
15
|
+
| `check_crawl_status` | Checks the status of a previously initiated async Firecrawl crawl job. |
|
|
16
|
+
| `cancel_crawl` | Cancels a currently running Firecrawl crawl job. |
|
|
17
|
+
| `start_batch_scrape` | Starts a batch scrape job for multiple URLs using Firecrawl. (Note: May map to multiple individual scrapes or a specific batch API endpoint if available) |
|
|
18
|
+
| `check_batch_scrape_status` | Checks the status of a previously initiated Firecrawl batch scrape job. |
|
|
19
|
+
| `quick_web_extract` | Performs a quick, synchronous extraction of data from one or more URLs using Firecrawl and returns the results directly. |
|
|
20
|
+
| `check_extract_status` | Checks the status of a previously initiated Firecrawl extraction job. |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .app import FirecrawlApp
|
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from loguru import logger
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
from firecrawl import Firecrawl
|
|
7
|
+
|
|
8
|
+
FirecrawlApiClient: type[Firecrawl] | None = Firecrawl
|
|
9
|
+
except ImportError:
|
|
10
|
+
FirecrawlApiClient = None
|
|
11
|
+
|
|
12
|
+
logger.error(
|
|
13
|
+
"Failed to import FirecrawlApp. Please ensure 'firecrawl-py' is installed."
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
from universal_mcp.applications.application import APIApplication
|
|
18
|
+
from universal_mcp.exceptions import NotAuthorizedError, ToolError
|
|
19
|
+
from universal_mcp.integrations import Integration
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class FirecrawlApp(APIApplication):
|
|
23
|
+
"""
|
|
24
|
+
Application for interacting with the Firecrawl service (firecrawl.dev)
|
|
25
|
+
to scrape web pages, perform searches, and manage crawl/batch scrape/extract jobs.
|
|
26
|
+
Requires a Firecrawl API key configured via integration.
|
|
27
|
+
Authentication is handled by the configured Integration, fetching the API key.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, integration: Integration | None = None, **kwargs: Any) -> None:
|
|
31
|
+
super().__init__(name="firecrawl", integration=integration, **kwargs)
|
|
32
|
+
self._firecrawl_api_key: str | None = None # Cache for the API key
|
|
33
|
+
if FirecrawlApiClient is None:
|
|
34
|
+
logger.warning(
|
|
35
|
+
"Firecrawl SDK is not available. Firecrawl tools will not function."
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def firecrawl_api_key(self) -> str:
|
|
40
|
+
"""
|
|
41
|
+
Retrieves and caches the Firecrawl API key from the integration.
|
|
42
|
+
Raises NotAuthorizedError if the key cannot be obtained.
|
|
43
|
+
"""
|
|
44
|
+
if self._firecrawl_api_key is None:
|
|
45
|
+
if not self.integration:
|
|
46
|
+
logger.error(
|
|
47
|
+
f"{self.name.capitalize()} App: Integration not configured."
|
|
48
|
+
)
|
|
49
|
+
raise NotAuthorizedError(
|
|
50
|
+
f"Integration not configured for {self.name.capitalize()} App. Cannot retrieve API key."
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
credentials = self.integration.get_credentials()
|
|
55
|
+
except NotAuthorizedError as e:
|
|
56
|
+
logger.error(
|
|
57
|
+
f"{self.name.capitalize()} App: Authorization error when fetching credentials: {e.message}"
|
|
58
|
+
)
|
|
59
|
+
raise # Re-raise the original NotAuthorizedError
|
|
60
|
+
except Exception as e:
|
|
61
|
+
logger.error(
|
|
62
|
+
f"{self.name.capitalize()} App: Unexpected error when fetching credentials: {e}",
|
|
63
|
+
exc_info=True,
|
|
64
|
+
)
|
|
65
|
+
raise NotAuthorizedError(
|
|
66
|
+
f"Failed to get {self.name.capitalize()} credentials: {e}"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
api_key = (
|
|
70
|
+
credentials.get("api_key")
|
|
71
|
+
or credentials.get("API_KEY") # Check common variations
|
|
72
|
+
or credentials.get("apiKey")
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
if not api_key:
|
|
76
|
+
logger.error(
|
|
77
|
+
f"{self.name.capitalize()} App: API key not found in credentials."
|
|
78
|
+
)
|
|
79
|
+
action_message = (
|
|
80
|
+
f"API key for {self.name.capitalize()} is missing. "
|
|
81
|
+
"Please ensure it's set in the store via MCP frontend or configuration."
|
|
82
|
+
)
|
|
83
|
+
if hasattr(self.integration, "authorize") and callable(
|
|
84
|
+
self.integration.authorize
|
|
85
|
+
):
|
|
86
|
+
try:
|
|
87
|
+
auth_details = self.integration.authorize()
|
|
88
|
+
if isinstance(auth_details, str):
|
|
89
|
+
action_message = auth_details
|
|
90
|
+
elif isinstance(auth_details, dict) and "url" in auth_details:
|
|
91
|
+
action_message = (
|
|
92
|
+
f"Please authorize via: {auth_details['url']}"
|
|
93
|
+
)
|
|
94
|
+
elif (
|
|
95
|
+
isinstance(auth_details, dict) and "message" in auth_details
|
|
96
|
+
):
|
|
97
|
+
action_message = auth_details["message"]
|
|
98
|
+
except Exception as auth_e:
|
|
99
|
+
logger.warning(
|
|
100
|
+
f"Could not retrieve specific authorization action for {self.name.capitalize()}: {auth_e}"
|
|
101
|
+
)
|
|
102
|
+
raise NotAuthorizedError(action_message)
|
|
103
|
+
|
|
104
|
+
self._firecrawl_api_key = api_key
|
|
105
|
+
logger.info(
|
|
106
|
+
f"{self.name.capitalize()} API Key successfully retrieved and cached."
|
|
107
|
+
)
|
|
108
|
+
assert self._firecrawl_api_key is not None
|
|
109
|
+
return self._firecrawl_api_key
|
|
110
|
+
|
|
111
|
+
def _get_client(self) -> Firecrawl:
|
|
112
|
+
"""
|
|
113
|
+
Initializes and returns the Firecrawl client after ensuring API key is set.
|
|
114
|
+
Raises NotAuthorizedError if API key cannot be obtained or SDK is not installed.
|
|
115
|
+
"""
|
|
116
|
+
if FirecrawlApiClient is None:
|
|
117
|
+
logger.error("Firecrawl SDK (firecrawl-py) is not available.")
|
|
118
|
+
raise ToolError(
|
|
119
|
+
"Firecrawl SDK (firecrawl-py) is not installed or failed to import."
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# The property self.firecrawl_api_key will raise NotAuthorizedError if key is missing/unretrievable
|
|
123
|
+
current_api_key = self.firecrawl_api_key
|
|
124
|
+
return FirecrawlApiClient(api_key=current_api_key)
|
|
125
|
+
|
|
126
|
+
def _handle_firecrawl_exception(self, e: Exception, operation_desc: str) -> str:
|
|
127
|
+
"""
|
|
128
|
+
Handles exceptions from Firecrawl operations, raising NotAuthorizedError for auth issues
|
|
129
|
+
and returning an error string for other issues.
|
|
130
|
+
This helper is designed to be used in tool methods.
|
|
131
|
+
"""
|
|
132
|
+
logger.error(
|
|
133
|
+
f"Firecrawl App: Error during {operation_desc}: {type(e).__name__} - {e}",
|
|
134
|
+
exc_info=True,
|
|
135
|
+
)
|
|
136
|
+
# Check for common authentication/authorization indicators
|
|
137
|
+
error_str = str(e).lower()
|
|
138
|
+
is_auth_error = (
|
|
139
|
+
"unauthorized" in error_str
|
|
140
|
+
or "api key" in error_str
|
|
141
|
+
or "authentication" in error_str
|
|
142
|
+
or (
|
|
143
|
+
hasattr(e, "response")
|
|
144
|
+
and hasattr(e.response, "status_code")
|
|
145
|
+
and e.response.status_code == 401
|
|
146
|
+
) # type: ignore
|
|
147
|
+
or (hasattr(e, "status_code") and e.status_code == 401) # type: ignore
|
|
148
|
+
)
|
|
149
|
+
if is_auth_error:
|
|
150
|
+
raise NotAuthorizedError(
|
|
151
|
+
f"Firecrawl API authentication/authorization failed for {operation_desc}: {e}"
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
return f"Error during {operation_desc}: {type(e).__name__} - {e}"
|
|
155
|
+
|
|
156
|
+
def scrape_url(self, url: str) -> Any:
|
|
157
|
+
"""
|
|
158
|
+
Scrapes a single URL using Firecrawl and returns the extracted data.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
url: The URL of the web page to scrape.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
A dictionary containing the scraped data on success,
|
|
165
|
+
or a string containing an error message on failure.
|
|
166
|
+
|
|
167
|
+
Raises:
|
|
168
|
+
NotAuthorizedError: If API key is missing or invalid.
|
|
169
|
+
ToolError: If the Firecrawl SDK is not installed.
|
|
170
|
+
|
|
171
|
+
Tags:
|
|
172
|
+
scrape, important
|
|
173
|
+
"""
|
|
174
|
+
logger.info(f"Attempting to scrape URL: {url}")
|
|
175
|
+
try:
|
|
176
|
+
client = self._get_client()
|
|
177
|
+
response_data = client.scrape(url=url)
|
|
178
|
+
logger.info(f"Successfully scraped URL: {url}")
|
|
179
|
+
return response_data
|
|
180
|
+
except NotAuthorizedError:
|
|
181
|
+
raise
|
|
182
|
+
except ToolError:
|
|
183
|
+
raise
|
|
184
|
+
except Exception as e:
|
|
185
|
+
error_msg = self._handle_firecrawl_exception(e, f"scraping URL {url}")
|
|
186
|
+
return error_msg
|
|
187
|
+
|
|
188
|
+
def search(self, query: str) -> dict[str, Any] | str:
|
|
189
|
+
"""
|
|
190
|
+
Performs a web search using Firecrawl's search capability.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
query: The search query string.
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
A dictionary containing the search results on success,
|
|
197
|
+
or a string containing an error message on failure.
|
|
198
|
+
|
|
199
|
+
Raises:
|
|
200
|
+
NotAuthorizedError: If API key is missing or invalid.
|
|
201
|
+
ToolError: If the Firecrawl SDK is not installed.
|
|
202
|
+
|
|
203
|
+
Tags:
|
|
204
|
+
search, important
|
|
205
|
+
"""
|
|
206
|
+
logger.info(f"Attempting Firecrawl search for query: {query}")
|
|
207
|
+
try:
|
|
208
|
+
client = self._get_client()
|
|
209
|
+
response = client.search(query=query)
|
|
210
|
+
logger.info(f"Successfully performed Firecrawl search for query: {query}")
|
|
211
|
+
return response # type: ignore
|
|
212
|
+
except NotAuthorizedError:
|
|
213
|
+
raise
|
|
214
|
+
except ToolError:
|
|
215
|
+
raise
|
|
216
|
+
except Exception as e:
|
|
217
|
+
return self._handle_firecrawl_exception(e, f"search for '{query}'")
|
|
218
|
+
|
|
219
|
+
def start_crawl(
|
|
220
|
+
self,
|
|
221
|
+
url: str,
|
|
222
|
+
) -> dict[str, Any] | str:
|
|
223
|
+
"""
|
|
224
|
+
Starts a async crawl job for a given URL using Firecrawl. Returns the job ID immediately.
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
url: The starting URL for the crawl.
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
A dictionary containing the job initiation response on success,
|
|
231
|
+
or a string containing an error message on failure.
|
|
232
|
+
|
|
233
|
+
Raises:
|
|
234
|
+
NotAuthorizedError: If API key is missing or invalid.
|
|
235
|
+
ToolError: If the Firecrawl SDK is not installed.
|
|
236
|
+
|
|
237
|
+
Tags:
|
|
238
|
+
crawl, async_job, start
|
|
239
|
+
"""
|
|
240
|
+
logger.info(f"Attempting to start Firecrawl crawl for URL: {url}")
|
|
241
|
+
try:
|
|
242
|
+
client = self._get_client()
|
|
243
|
+
response = client.start_crawl(
|
|
244
|
+
url=url,
|
|
245
|
+
)
|
|
246
|
+
job_id = response.id
|
|
247
|
+
logger.info(
|
|
248
|
+
f"Successfully started Firecrawl crawl for URL {url}, Job ID: {job_id}"
|
|
249
|
+
)
|
|
250
|
+
return response # type: ignore
|
|
251
|
+
except NotAuthorizedError:
|
|
252
|
+
raise
|
|
253
|
+
except ToolError:
|
|
254
|
+
raise
|
|
255
|
+
except Exception as e:
|
|
256
|
+
return self._handle_firecrawl_exception(e, f"starting crawl for URL {url}") # type: ignore
|
|
257
|
+
|
|
258
|
+
def check_crawl_status(self, job_id: str) -> dict[str, Any] | str:
|
|
259
|
+
"""
|
|
260
|
+
Checks the status of a previously initiated async Firecrawl crawl job.
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
job_id: The ID of the crawl job to check.
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
A dictionary containing the job status details on success,
|
|
267
|
+
or a string containing an error message on failure.
|
|
268
|
+
|
|
269
|
+
Raises:
|
|
270
|
+
NotAuthorizedError: If API key is missing or invalid.
|
|
271
|
+
ToolError: If the Firecrawl SDK is not installed.
|
|
272
|
+
|
|
273
|
+
Tags:
|
|
274
|
+
crawl, async_job, status
|
|
275
|
+
"""
|
|
276
|
+
logger.info(f"Attempting to check Firecrawl crawl status for job ID: {job_id}")
|
|
277
|
+
try:
|
|
278
|
+
client = self._get_client()
|
|
279
|
+
status = client.get_crawl_status(job_id=job_id)
|
|
280
|
+
logger.info(
|
|
281
|
+
f"Successfully checked Firecrawl crawl status for job ID: {job_id}"
|
|
282
|
+
)
|
|
283
|
+
return status # type: ignore
|
|
284
|
+
except NotAuthorizedError:
|
|
285
|
+
raise
|
|
286
|
+
except ToolError:
|
|
287
|
+
raise
|
|
288
|
+
except Exception as e:
|
|
289
|
+
return self._handle_firecrawl_exception(
|
|
290
|
+
e, f"checking crawl status for job ID {job_id}"
|
|
291
|
+
) # type: ignore
|
|
292
|
+
|
|
293
|
+
def cancel_crawl(self, job_id: str) -> dict[str, Any] | str:
|
|
294
|
+
"""
|
|
295
|
+
Cancels a currently running Firecrawl crawl job.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
job_id: The ID of the crawl job to cancel.
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
A dictionary confirming the cancellation status on success,
|
|
302
|
+
or a string containing an error message on failure.
|
|
303
|
+
(Note: This functionality might depend on Firecrawl API capabilities)
|
|
304
|
+
|
|
305
|
+
Raises:
|
|
306
|
+
NotAuthorizedError: If API key is missing or invalid.
|
|
307
|
+
ToolError: If the Firecrawl SDK is not installed or operation not supported.
|
|
308
|
+
|
|
309
|
+
Tags:
|
|
310
|
+
crawl, async_job, management, cancel
|
|
311
|
+
"""
|
|
312
|
+
logger.info(f"Attempting to cancel Firecrawl crawl job ID: {job_id}")
|
|
313
|
+
try:
|
|
314
|
+
client = self._get_client()
|
|
315
|
+
response = client.cancel_crawl(crawl_id=job_id)
|
|
316
|
+
logger.info(
|
|
317
|
+
f"Successfully issued cancel command for Firecrawl crawl job ID: {job_id}"
|
|
318
|
+
)
|
|
319
|
+
return response # type: ignore
|
|
320
|
+
except NotAuthorizedError:
|
|
321
|
+
raise
|
|
322
|
+
except ToolError:
|
|
323
|
+
raise
|
|
324
|
+
except Exception as e:
|
|
325
|
+
return self._handle_firecrawl_exception(
|
|
326
|
+
e, f"cancelling crawl job ID {job_id}"
|
|
327
|
+
) # type: ignore
|
|
328
|
+
|
|
329
|
+
def start_batch_scrape(
|
|
330
|
+
self,
|
|
331
|
+
urls: list[str],
|
|
332
|
+
) -> dict[str, Any] | str:
|
|
333
|
+
"""
|
|
334
|
+
Starts a batch scrape job for multiple URLs using Firecrawl. (Note: May map to multiple individual scrapes or a specific batch API endpoint if available)
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
urls: A list of URLs to scrape.
|
|
338
|
+
|
|
339
|
+
Returns:
|
|
340
|
+
A dictionary containing the job initiation response (e.g., a batch job ID or list of results/job IDs) on success,
|
|
341
|
+
or a string containing an error message on failure.
|
|
342
|
+
|
|
343
|
+
Raises:
|
|
344
|
+
NotAuthorizedError: If API key is missing or invalid.
|
|
345
|
+
ToolError: If the Firecrawl SDK is not installed.
|
|
346
|
+
|
|
347
|
+
Tags:
|
|
348
|
+
scrape, batch, async_job, start
|
|
349
|
+
"""
|
|
350
|
+
logger.info(f"Attempting to start Firecrawl batch scrape for {len(urls)} URLs.")
|
|
351
|
+
try:
|
|
352
|
+
client = self._get_client()
|
|
353
|
+
response = client.start_batch_scrape(urls=urls)
|
|
354
|
+
logger.info(
|
|
355
|
+
f"Successfully started Firecrawl batch scrape for {len(urls)} URLs."
|
|
356
|
+
)
|
|
357
|
+
return response # type: ignore
|
|
358
|
+
except NotAuthorizedError:
|
|
359
|
+
raise
|
|
360
|
+
except ToolError:
|
|
361
|
+
raise
|
|
362
|
+
except Exception as e:
|
|
363
|
+
return self._handle_firecrawl_exception(
|
|
364
|
+
e, f"starting batch scrape for {len(urls)} URLs"
|
|
365
|
+
) # type: ignore
|
|
366
|
+
|
|
367
|
+
def check_batch_scrape_status(self, job_id: str) -> dict[str, Any] | str:
|
|
368
|
+
"""
|
|
369
|
+
Checks the status of a previously initiated Firecrawl batch scrape job.
|
|
370
|
+
|
|
371
|
+
Args:
|
|
372
|
+
job_id: The ID of the batch scrape job to check.
|
|
373
|
+
|
|
374
|
+
Returns:
|
|
375
|
+
A dictionary containing the job status details on success,
|
|
376
|
+
or a string containing an error message on failure.
|
|
377
|
+
|
|
378
|
+
Raises:
|
|
379
|
+
NotAuthorizedError: If API key is missing or invalid.
|
|
380
|
+
ToolError: If the Firecrawl SDK is not installed or operation not supported.
|
|
381
|
+
|
|
382
|
+
Tags:
|
|
383
|
+
scrape, batch, async_job, status
|
|
384
|
+
"""
|
|
385
|
+
logger.info(
|
|
386
|
+
f"Attempting to check Firecrawl batch scrape status for job ID: {job_id}"
|
|
387
|
+
)
|
|
388
|
+
try:
|
|
389
|
+
client = self._get_client()
|
|
390
|
+
status = client.get_batch_scrape_status(job_id=job_id)
|
|
391
|
+
logger.info(
|
|
392
|
+
f"Successfully checked Firecrawl batch scrape status for job ID: {job_id}"
|
|
393
|
+
)
|
|
394
|
+
return status # type: ignore
|
|
395
|
+
except NotAuthorizedError:
|
|
396
|
+
raise
|
|
397
|
+
except ToolError:
|
|
398
|
+
raise
|
|
399
|
+
except Exception as e:
|
|
400
|
+
return self._handle_firecrawl_exception(
|
|
401
|
+
e, f"checking batch scrape status for job ID {job_id}"
|
|
402
|
+
) # type: ignore
|
|
403
|
+
|
|
404
|
+
def quick_web_extract(
|
|
405
|
+
self,
|
|
406
|
+
urls: list[str],
|
|
407
|
+
prompt: str | None = None,
|
|
408
|
+
schema: Any | None = None,
|
|
409
|
+
system_prompt: str | None = None,
|
|
410
|
+
allow_external_links: bool | None = False,
|
|
411
|
+
) -> dict[str, Any]:
|
|
412
|
+
"""
|
|
413
|
+
Performs a quick, synchronous extraction of data from one or more URLs using Firecrawl and returns the results directly.
|
|
414
|
+
|
|
415
|
+
Args:
|
|
416
|
+
urls: A list of URLs to extract data from.
|
|
417
|
+
prompt: Optional custom extraction prompt describing what data to extract.
|
|
418
|
+
schema: Optional JSON schema or Pydantic model for the desired output structure.
|
|
419
|
+
system_prompt: Optional system context for the extraction.
|
|
420
|
+
allow_external_links: Optional boolean to allow following external links.
|
|
421
|
+
|
|
422
|
+
Returns:
|
|
423
|
+
A dictionary containing the extracted data on success.
|
|
424
|
+
|
|
425
|
+
Raises:
|
|
426
|
+
NotAuthorizedError: If API key is missing or invalid.
|
|
427
|
+
ToolError: If the Firecrawl SDK is not installed or extraction fails.
|
|
428
|
+
|
|
429
|
+
Tags:
|
|
430
|
+
extract, ai, sync, quick, important
|
|
431
|
+
"""
|
|
432
|
+
logger.info(
|
|
433
|
+
f"Attempting quick web extraction for {len(urls)} URLs with prompt: {prompt is not None}, schema: {schema is not None}."
|
|
434
|
+
)
|
|
435
|
+
try:
|
|
436
|
+
client = self._get_client()
|
|
437
|
+
response = client.extract(
|
|
438
|
+
urls=urls,
|
|
439
|
+
prompt=prompt,
|
|
440
|
+
schema=schema,
|
|
441
|
+
system_prompt=system_prompt,
|
|
442
|
+
allow_external_links=allow_external_links,
|
|
443
|
+
)
|
|
444
|
+
logger.info(
|
|
445
|
+
f"Successfully completed quick web extraction for {len(urls)} URLs."
|
|
446
|
+
)
|
|
447
|
+
return response # type: ignore
|
|
448
|
+
except NotAuthorizedError:
|
|
449
|
+
logger.error("Firecrawl API key missing or invalid.")
|
|
450
|
+
raise
|
|
451
|
+
except ToolError:
|
|
452
|
+
logger.error("Firecrawl SDK not installed.")
|
|
453
|
+
raise
|
|
454
|
+
except Exception as e:
|
|
455
|
+
error_message = self._handle_firecrawl_exception(
|
|
456
|
+
e, f"quick web extraction for {len(urls)} URLs"
|
|
457
|
+
)
|
|
458
|
+
logger.error(f"Failed to perform quick web extraction: {error_message}")
|
|
459
|
+
if error_message:
|
|
460
|
+
raise ToolError(error_message)
|
|
461
|
+
else:
|
|
462
|
+
raise ToolError(
|
|
463
|
+
f"Quick web extraction failed for {len(urls)} URLs: {e}"
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
def check_extract_status(self, job_id: str) -> dict[str, Any] | str:
|
|
467
|
+
"""
|
|
468
|
+
Checks the status of a previously initiated Firecrawl extraction job.
|
|
469
|
+
|
|
470
|
+
Args:
|
|
471
|
+
job_id: The ID of the extraction job to check.
|
|
472
|
+
|
|
473
|
+
Returns:
|
|
474
|
+
A dictionary containing the job status details on success,
|
|
475
|
+
or a string containing an error message on failure.
|
|
476
|
+
|
|
477
|
+
Raises:
|
|
478
|
+
NotAuthorizedError: If API key is missing or invalid.
|
|
479
|
+
ToolError: If the Firecrawl SDK is not installed or operation not supported.
|
|
480
|
+
|
|
481
|
+
Tags:
|
|
482
|
+
extract, ai, async_job, status
|
|
483
|
+
"""
|
|
484
|
+
logger.info(
|
|
485
|
+
f"Attempting to check Firecrawl extraction status for job ID: {job_id}"
|
|
486
|
+
)
|
|
487
|
+
try:
|
|
488
|
+
client = self._get_client()
|
|
489
|
+
status = client.get_extract_status(job_id=job_id)
|
|
490
|
+
logger.info(
|
|
491
|
+
f"Successfully checked Firecrawl extraction status for job ID: {job_id}"
|
|
492
|
+
)
|
|
493
|
+
return status # type: ignore
|
|
494
|
+
except NotAuthorizedError:
|
|
495
|
+
raise
|
|
496
|
+
except ToolError:
|
|
497
|
+
raise
|
|
498
|
+
except Exception as e:
|
|
499
|
+
return self._handle_firecrawl_exception(
|
|
500
|
+
e, f"checking extraction status for job ID {job_id}"
|
|
501
|
+
) # type: ignore
|
|
502
|
+
|
|
503
|
+
def list_tools(self):
|
|
504
|
+
return [
|
|
505
|
+
self.scrape_url,
|
|
506
|
+
self.search,
|
|
507
|
+
self.start_crawl,
|
|
508
|
+
self.check_crawl_status,
|
|
509
|
+
self.cancel_crawl,
|
|
510
|
+
self.start_batch_scrape,
|
|
511
|
+
self.check_batch_scrape_status,
|
|
512
|
+
self.quick_web_extract,
|
|
513
|
+
self.check_extract_status,
|
|
514
|
+
]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# FirefliesApp MCP Server
|
|
2
|
+
|
|
3
|
+
An MCP Server for the FirefliesApp API.
|
|
4
|
+
|
|
5
|
+
## 🛠️ Tool List
|
|
6
|
+
|
|
7
|
+
This is automatically generated from OpenAPI schema for the FirefliesApp API.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
| Tool | Description |
|
|
11
|
+
|------|-------------|
|
|
12
|
+
| `get_team_analytics` | Fetches team analytics data within a specified time range. |
|
|
13
|
+
| `get_ai_apps_outputs` | Fetches AI Apps outputs for a given transcript. |
|
|
14
|
+
| `get_user_details` | Fetches details for a specific user. |
|
|
15
|
+
| `list_users` | Fetches a list of users in the workspace. |
|
|
16
|
+
| `get_transcript_details` | Fetches details for a specific transcript. |
|
|
17
|
+
| `list_transcripts` | Fetches a list of transcripts, optionally filtered by user ID. |
|
|
18
|
+
| `get_bite_details` | Fetches details for a specific bite (soundbite/clip). |
|
|
19
|
+
| `list_bites` | Fetches a list of bites, optionally filtered to the current user's bites. |
|
|
20
|
+
| `add_to_live_meeting` | Adds Fireflies.ai to a live meeting. |
|
|
21
|
+
| `create_bite` | Creates a bite (soundbite/clip) from a transcript. |
|
|
22
|
+
| `delete_transcript` | Deletes a transcript. |
|
|
23
|
+
| `set_user_role` | Sets the role for a user. |
|
|
24
|
+
| `upload_audio` | Uploads an audio file for transcription. |
|
|
25
|
+
| `update_meeting_title` | Updates the title of a meeting (transcript). |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .app import FirefliesApp
|