universal-mcp-applications 0.1.30__py3-none-any.whl → 0.1.36rc1__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/ahrefs/app.py +52 -198
- universal_mcp/applications/airtable/app.py +23 -122
- universal_mcp/applications/apollo/app.py +111 -464
- universal_mcp/applications/asana/app.py +417 -1567
- universal_mcp/applications/aws_s3/app.py +33 -100
- universal_mcp/applications/bill/app.py +546 -1957
- universal_mcp/applications/box/app.py +1068 -3981
- universal_mcp/applications/braze/app.py +364 -1430
- universal_mcp/applications/browser_use/app.py +2 -8
- universal_mcp/applications/cal_com_v2/app.py +207 -625
- universal_mcp/applications/calendly/app.py +61 -200
- universal_mcp/applications/canva/app.py +45 -110
- universal_mcp/applications/clickup/app.py +207 -674
- universal_mcp/applications/coda/app.py +146 -426
- universal_mcp/applications/confluence/app.py +310 -1098
- universal_mcp/applications/contentful/app.py +36 -151
- universal_mcp/applications/crustdata/app.py +28 -107
- universal_mcp/applications/dialpad/app.py +283 -756
- universal_mcp/applications/digitalocean/app.py +1766 -5777
- universal_mcp/applications/domain_checker/app.py +3 -54
- universal_mcp/applications/e2b/app.py +14 -64
- universal_mcp/applications/elevenlabs/app.py +9 -47
- universal_mcp/applications/exa/app.py +6 -17
- universal_mcp/applications/falai/app.py +23 -100
- universal_mcp/applications/figma/app.py +53 -137
- universal_mcp/applications/file_system/app.py +2 -13
- universal_mcp/applications/firecrawl/app.py +51 -152
- universal_mcp/applications/fireflies/app.py +59 -281
- universal_mcp/applications/fpl/app.py +91 -528
- universal_mcp/applications/fpl/utils/fixtures.py +15 -49
- universal_mcp/applications/fpl/utils/helper.py +25 -89
- universal_mcp/applications/fpl/utils/league_utils.py +20 -64
- universal_mcp/applications/ghost_content/app.py +52 -161
- universal_mcp/applications/github/app.py +19 -56
- universal_mcp/applications/gong/app.py +88 -248
- universal_mcp/applications/google_calendar/app.py +16 -68
- universal_mcp/applications/google_docs/app.py +88 -188
- universal_mcp/applications/google_drive/app.py +140 -462
- universal_mcp/applications/google_gemini/app.py +12 -64
- universal_mcp/applications/google_mail/app.py +28 -157
- universal_mcp/applications/google_searchconsole/app.py +15 -48
- universal_mcp/applications/google_sheet/app.py +101 -578
- universal_mcp/applications/google_sheet/helper.py +10 -37
- universal_mcp/applications/hashnode/app.py +57 -269
- universal_mcp/applications/heygen/app.py +44 -122
- universal_mcp/applications/http_tools/app.py +10 -32
- universal_mcp/applications/hubspot/api_segments/crm_api.py +460 -1573
- universal_mcp/applications/hubspot/api_segments/marketing_api.py +74 -262
- universal_mcp/applications/hubspot/app.py +23 -87
- universal_mcp/applications/jira/app.py +2071 -7986
- universal_mcp/applications/klaviyo/app.py +494 -1376
- universal_mcp/applications/linkedin/README.md +9 -2
- universal_mcp/applications/linkedin/app.py +392 -212
- universal_mcp/applications/mailchimp/app.py +450 -1605
- universal_mcp/applications/markitdown/app.py +8 -20
- universal_mcp/applications/miro/app.py +217 -699
- universal_mcp/applications/ms_teams/app.py +64 -186
- universal_mcp/applications/neon/app.py +86 -192
- universal_mcp/applications/notion/app.py +21 -36
- universal_mcp/applications/onedrive/app.py +14 -36
- universal_mcp/applications/openai/app.py +42 -165
- universal_mcp/applications/outlook/app.py +16 -76
- universal_mcp/applications/perplexity/app.py +4 -19
- universal_mcp/applications/pipedrive/app.py +832 -3142
- universal_mcp/applications/posthog/app.py +163 -432
- universal_mcp/applications/reddit/app.py +40 -139
- universal_mcp/applications/resend/app.py +41 -107
- universal_mcp/applications/retell/app.py +14 -41
- universal_mcp/applications/rocketlane/app.py +221 -934
- universal_mcp/applications/scraper/README.md +7 -4
- universal_mcp/applications/scraper/app.py +216 -102
- universal_mcp/applications/semanticscholar/app.py +22 -64
- universal_mcp/applications/semrush/app.py +43 -77
- universal_mcp/applications/sendgrid/app.py +512 -1262
- universal_mcp/applications/sentry/app.py +271 -906
- universal_mcp/applications/serpapi/app.py +40 -143
- universal_mcp/applications/sharepoint/app.py +15 -37
- universal_mcp/applications/shopify/app.py +1551 -4287
- universal_mcp/applications/shortcut/app.py +155 -417
- universal_mcp/applications/slack/app.py +50 -101
- universal_mcp/applications/spotify/app.py +126 -325
- universal_mcp/applications/supabase/app.py +104 -213
- universal_mcp/applications/tavily/app.py +1 -1
- universal_mcp/applications/trello/app.py +693 -2656
- universal_mcp/applications/twilio/app.py +14 -50
- universal_mcp/applications/twitter/api_segments/compliance_api.py +4 -14
- universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +6 -18
- universal_mcp/applications/twitter/api_segments/likes_api.py +1 -3
- universal_mcp/applications/twitter/api_segments/lists_api.py +5 -15
- universal_mcp/applications/twitter/api_segments/trends_api.py +1 -3
- universal_mcp/applications/twitter/api_segments/tweets_api.py +9 -31
- universal_mcp/applications/twitter/api_segments/usage_api.py +1 -5
- universal_mcp/applications/twitter/api_segments/users_api.py +14 -42
- universal_mcp/applications/whatsapp/app.py +35 -186
- universal_mcp/applications/whatsapp/audio.py +2 -6
- universal_mcp/applications/whatsapp/whatsapp.py +17 -51
- universal_mcp/applications/whatsapp_business/app.py +70 -283
- universal_mcp/applications/wrike/app.py +45 -118
- universal_mcp/applications/yahoo_finance/app.py +19 -65
- universal_mcp/applications/youtube/app.py +75 -261
- universal_mcp/applications/zenquotes/app.py +2 -2
- {universal_mcp_applications-0.1.30.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/METADATA +2 -2
- {universal_mcp_applications-0.1.30.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/RECORD +105 -105
- {universal_mcp_applications-0.1.30.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/WHEEL +0 -0
- {universal_mcp_applications-0.1.30.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from collections.abc import Callable
|
|
2
2
|
from typing import Any
|
|
3
|
-
|
|
4
3
|
from loguru import logger
|
|
5
4
|
from universal_mcp.applications.application import APIApplication
|
|
6
5
|
from universal_mcp.integrations import Integration
|
|
@@ -26,8 +25,8 @@ class GhostContentApp(APIApplication):
|
|
|
26
25
|
"""
|
|
27
26
|
super().__init__(name="ghost_content", integration=integration)
|
|
28
27
|
self._base_url = None
|
|
29
|
-
self._api_key = None
|
|
30
|
-
self._version = None
|
|
28
|
+
self._api_key = None
|
|
29
|
+
self._version = None
|
|
31
30
|
|
|
32
31
|
@property
|
|
33
32
|
def base_url(self) -> str:
|
|
@@ -39,13 +38,8 @@ class GhostContentApp(APIApplication):
|
|
|
39
38
|
credentials = self.integration.get_credentials()
|
|
40
39
|
ghost_url = credentials.get("url") or credentials.get("admin_domain")
|
|
41
40
|
if not ghost_url:
|
|
42
|
-
logger.error(
|
|
43
|
-
|
|
44
|
-
)
|
|
45
|
-
raise ValueError(
|
|
46
|
-
"Integration credentials must include 'url' or 'admin_domain' for the Ghost site."
|
|
47
|
-
)
|
|
48
|
-
|
|
41
|
+
logger.error("GhostContentApp: Missing 'url' or 'admin_domain' in integration credentials.")
|
|
42
|
+
raise ValueError("Integration credentials must include 'url' or 'admin_domain' for the Ghost site.")
|
|
49
43
|
self._base_url = f"{ghost_url.rstrip('/')}/api/content/"
|
|
50
44
|
logger.info(f"GhostContentApp: Constructed base URL as {self._base_url}")
|
|
51
45
|
return self._base_url
|
|
@@ -70,18 +64,10 @@ class GhostContentApp(APIApplication):
|
|
|
70
64
|
"""
|
|
71
65
|
if not self._api_key:
|
|
72
66
|
credentials = self.integration.get_credentials()
|
|
73
|
-
api_key = (
|
|
74
|
-
credentials.get("key")
|
|
75
|
-
or credentials.get("api_key")
|
|
76
|
-
or credentials.get("API_KEY")
|
|
77
|
-
)
|
|
67
|
+
api_key = credentials.get("key") or credentials.get("api_key") or credentials.get("API_KEY")
|
|
78
68
|
if not api_key:
|
|
79
|
-
logger.error(
|
|
80
|
-
|
|
81
|
-
)
|
|
82
|
-
raise ValueError(
|
|
83
|
-
"Integration credentials must include the Ghost Content API 'key'."
|
|
84
|
-
)
|
|
69
|
+
logger.error("GhostContentApp: Content API key ('key') not found in integration credentials.")
|
|
70
|
+
raise ValueError("Integration credentials must include the Ghost Content API 'key'.")
|
|
85
71
|
self._api_key = api_key
|
|
86
72
|
return self._api_key
|
|
87
73
|
|
|
@@ -95,10 +81,8 @@ class GhostContentApp(APIApplication):
|
|
|
95
81
|
credentials = self.integration.get_credentials()
|
|
96
82
|
version = credentials.get("api_version")
|
|
97
83
|
if not version:
|
|
98
|
-
logger.warning(
|
|
99
|
-
|
|
100
|
-
)
|
|
101
|
-
version = "v5.0" # Default to a common version if not specified
|
|
84
|
+
logger.warning("GhostContentApp: 'version' not found in integration credentials. Defaulting to 'v5.0'.")
|
|
85
|
+
version = "v5.0"
|
|
102
86
|
self._version = version
|
|
103
87
|
return self._version
|
|
104
88
|
|
|
@@ -107,37 +91,32 @@ class GhostContentApp(APIApplication):
|
|
|
107
91
|
Get the headers for Ghost Content API requests.
|
|
108
92
|
Overrides the base class method to include the `Accept-Version` header.
|
|
109
93
|
"""
|
|
110
|
-
headers = super()._get_headers()
|
|
111
|
-
|
|
112
|
-
# Add the Accept-Version header as per Ghost Content API documentation
|
|
94
|
+
headers = super()._get_headers()
|
|
113
95
|
headers["Accept-Version"] = self._get_version
|
|
114
|
-
logger.debug(
|
|
115
|
-
f"GhostContentApp: Using Accept-Version: {self._get_version} in headers."
|
|
116
|
-
)
|
|
96
|
+
logger.debug(f"GhostContentApp: Using Accept-Version: {self._get_version} in headers.")
|
|
117
97
|
return headers
|
|
118
98
|
|
|
119
99
|
def _build_common_params(
|
|
120
100
|
self,
|
|
121
101
|
include: list[str] | None = None,
|
|
122
102
|
fields: list[str] | None = None,
|
|
123
|
-
filter: str | None = None,
|
|
103
|
+
filter: str | None = None,
|
|
124
104
|
limit: int | None = None,
|
|
125
105
|
order: str | None = None,
|
|
126
106
|
page: int | None = None,
|
|
127
|
-
formats: list[str] | None = None,
|
|
128
|
-
visibility: str | None = None,
|
|
107
|
+
formats: list[str] | None = None,
|
|
108
|
+
visibility: str | None = None,
|
|
129
109
|
) -> dict[str, Any]:
|
|
130
110
|
"""
|
|
131
111
|
Helper to build common query parameters for Ghost Content API requests,
|
|
132
112
|
including the mandatory API key.
|
|
133
113
|
"""
|
|
134
114
|
params: dict[str, Any] = {"key": self._get_api_key}
|
|
135
|
-
|
|
136
115
|
if include:
|
|
137
116
|
params["include"] = ",".join(include)
|
|
138
117
|
if fields:
|
|
139
118
|
params["fields"] = ",".join(fields)
|
|
140
|
-
if filter:
|
|
119
|
+
if filter:
|
|
141
120
|
params["filter"] = filter
|
|
142
121
|
if limit is not None:
|
|
143
122
|
params["limit"] = limit
|
|
@@ -151,8 +130,7 @@ class GhostContentApp(APIApplication):
|
|
|
151
130
|
params["visibility"] = visibility
|
|
152
131
|
return params
|
|
153
132
|
|
|
154
|
-
|
|
155
|
-
def browse_posts(
|
|
133
|
+
async def browse_posts(
|
|
156
134
|
self,
|
|
157
135
|
include: list[str] | None = None,
|
|
158
136
|
fields: list[str] | None = None,
|
|
@@ -161,7 +139,7 @@ class GhostContentApp(APIApplication):
|
|
|
161
139
|
page: int | None = None,
|
|
162
140
|
order: str | None = None,
|
|
163
141
|
formats: list[str] | None = None,
|
|
164
|
-
) -> dict[str, Any]:
|
|
142
|
+
) -> dict[str, Any]:
|
|
165
143
|
"""
|
|
166
144
|
Retrieves and browses posts from a data source based on provided parameters.
|
|
167
145
|
|
|
@@ -184,26 +162,15 @@ class GhostContentApp(APIApplication):
|
|
|
184
162
|
browse, fetch, posts, management, important
|
|
185
163
|
"""
|
|
186
164
|
url = f"{self.base_url}posts/"
|
|
187
|
-
# Removed 'visibility' from params as it's not in the method signature
|
|
188
165
|
params = self._build_common_params(
|
|
189
|
-
include=include,
|
|
190
|
-
fields=fields,
|
|
191
|
-
filter=filter,
|
|
192
|
-
limit=limit,
|
|
193
|
-
page=page,
|
|
194
|
-
order=order,
|
|
195
|
-
formats=formats,
|
|
166
|
+
include=include, fields=fields, filter=filter, limit=limit, page=page, order=order, formats=formats
|
|
196
167
|
)
|
|
197
168
|
response = self._get(url, params=params)
|
|
198
169
|
return response.json()
|
|
199
170
|
|
|
200
|
-
def read_post_by_id(
|
|
201
|
-
self,
|
|
202
|
-
|
|
203
|
-
include: list[str] | None = None,
|
|
204
|
-
fields: list[str] | None = None,
|
|
205
|
-
formats: list[str] | None = None,
|
|
206
|
-
) -> dict[str, Any]: # Changed return type
|
|
171
|
+
async def read_post_by_id(
|
|
172
|
+
self, id: str, include: list[str] | None = None, fields: list[str] | None = None, formats: list[str] | None = None
|
|
173
|
+
) -> dict[str, Any]:
|
|
207
174
|
"""
|
|
208
175
|
Retrieves a post by its ID, optionally including additional data or specific fields.
|
|
209
176
|
|
|
@@ -223,19 +190,13 @@ class GhostContentApp(APIApplication):
|
|
|
223
190
|
read, post, management
|
|
224
191
|
"""
|
|
225
192
|
url = f"{self.base_url}posts/{id}/"
|
|
226
|
-
params = self._build_common_params(
|
|
227
|
-
include=include, fields=fields, formats=formats
|
|
228
|
-
)
|
|
193
|
+
params = self._build_common_params(include=include, fields=fields, formats=formats)
|
|
229
194
|
response = self._get(url, params=params)
|
|
230
195
|
return response.json()
|
|
231
196
|
|
|
232
|
-
def read_post_by_slug(
|
|
233
|
-
self,
|
|
234
|
-
|
|
235
|
-
include: list[str] | None = None,
|
|
236
|
-
fields: list[str] | None = None,
|
|
237
|
-
formats: list[str] | None = None,
|
|
238
|
-
) -> dict[str, Any]: # Changed return type
|
|
197
|
+
async def read_post_by_slug(
|
|
198
|
+
self, slug: str, include: list[str] | None = None, fields: list[str] | None = None, formats: list[str] | None = None
|
|
199
|
+
) -> dict[str, Any]:
|
|
239
200
|
"""
|
|
240
201
|
Retrieves a post by its slug, with optional parameters to specify included data, select specific fields, or request particular data formats.
|
|
241
202
|
|
|
@@ -255,14 +216,11 @@ class GhostContentApp(APIApplication):
|
|
|
255
216
|
read, post, fetch, management
|
|
256
217
|
"""
|
|
257
218
|
url = f"{self.base_url}posts/slug/{slug}/"
|
|
258
|
-
params = self._build_common_params(
|
|
259
|
-
include=include, fields=fields, formats=formats
|
|
260
|
-
)
|
|
219
|
+
params = self._build_common_params(include=include, fields=fields, formats=formats)
|
|
261
220
|
response = self._get(url, params=params)
|
|
262
221
|
return response.json()
|
|
263
222
|
|
|
264
|
-
|
|
265
|
-
def browse_authors(
|
|
223
|
+
async def browse_authors(
|
|
266
224
|
self,
|
|
267
225
|
include: list[str] | None = None,
|
|
268
226
|
fields: list[str] | None = None,
|
|
@@ -270,7 +228,7 @@ class GhostContentApp(APIApplication):
|
|
|
270
228
|
limit: int | None = None,
|
|
271
229
|
page: int | None = None,
|
|
272
230
|
order: str | None = None,
|
|
273
|
-
) -> dict[str, Any]:
|
|
231
|
+
) -> dict[str, Any]:
|
|
274
232
|
"""
|
|
275
233
|
Browse authors using various filtering and pagination options.
|
|
276
234
|
|
|
@@ -292,23 +250,11 @@ class GhostContentApp(APIApplication):
|
|
|
292
250
|
list, management, important
|
|
293
251
|
"""
|
|
294
252
|
url = f"{self.base_url}authors/"
|
|
295
|
-
params = self._build_common_params(
|
|
296
|
-
include=include,
|
|
297
|
-
fields=fields,
|
|
298
|
-
filter=filter,
|
|
299
|
-
limit=limit,
|
|
300
|
-
page=page,
|
|
301
|
-
order=order,
|
|
302
|
-
)
|
|
253
|
+
params = self._build_common_params(include=include, fields=fields, filter=filter, limit=limit, page=page, order=order)
|
|
303
254
|
response = self._get(url, params=params)
|
|
304
255
|
return response.json()
|
|
305
256
|
|
|
306
|
-
def read_author_by_id(
|
|
307
|
-
self,
|
|
308
|
-
id: str,
|
|
309
|
-
include: list[str] | None = None,
|
|
310
|
-
fields: list[str] | None = None,
|
|
311
|
-
) -> dict[str, Any]: # Changed return type
|
|
257
|
+
async def read_author_by_id(self, id: str, include: list[str] | None = None, fields: list[str] | None = None) -> dict[str, Any]:
|
|
312
258
|
"""
|
|
313
259
|
Read an author from the database by their unique ID.
|
|
314
260
|
|
|
@@ -331,12 +277,7 @@ class GhostContentApp(APIApplication):
|
|
|
331
277
|
response = self._get(url, params=params)
|
|
332
278
|
return response.json()
|
|
333
279
|
|
|
334
|
-
def read_author_by_slug(
|
|
335
|
-
self,
|
|
336
|
-
slug: str,
|
|
337
|
-
include: list[str] | None = None,
|
|
338
|
-
fields: list[str] | None = None,
|
|
339
|
-
) -> dict[str, Any]: # Changed return type
|
|
280
|
+
async def read_author_by_slug(self, slug: str, include: list[str] | None = None, fields: list[str] | None = None) -> dict[str, Any]:
|
|
340
281
|
"""
|
|
341
282
|
Retrieve an author's information by their slug.
|
|
342
283
|
|
|
@@ -359,8 +300,7 @@ class GhostContentApp(APIApplication):
|
|
|
359
300
|
response = self._get(url, params=params)
|
|
360
301
|
return response.json()
|
|
361
302
|
|
|
362
|
-
|
|
363
|
-
def browse_tags(
|
|
303
|
+
async def browse_tags(
|
|
364
304
|
self,
|
|
365
305
|
include: list[str] | None = None,
|
|
366
306
|
fields: list[str] | None = None,
|
|
@@ -368,7 +308,7 @@ class GhostContentApp(APIApplication):
|
|
|
368
308
|
limit: int | None = None,
|
|
369
309
|
page: int | None = None,
|
|
370
310
|
order: str | None = None,
|
|
371
|
-
) -> dict[str, Any]:
|
|
311
|
+
) -> dict[str, Any]:
|
|
372
312
|
"""
|
|
373
313
|
Browse and retrieve tags based on specified parameters.
|
|
374
314
|
|
|
@@ -390,23 +330,11 @@ class GhostContentApp(APIApplication):
|
|
|
390
330
|
browse, tags, management, important
|
|
391
331
|
"""
|
|
392
332
|
url = f"{self.base_url}tags/"
|
|
393
|
-
params = self._build_common_params(
|
|
394
|
-
include=include,
|
|
395
|
-
fields=fields,
|
|
396
|
-
filter=filter,
|
|
397
|
-
limit=limit,
|
|
398
|
-
page=page,
|
|
399
|
-
order=order,
|
|
400
|
-
)
|
|
333
|
+
params = self._build_common_params(include=include, fields=fields, filter=filter, limit=limit, page=page, order=order)
|
|
401
334
|
response = self._get(url, params=params)
|
|
402
335
|
return response.json()
|
|
403
336
|
|
|
404
|
-
def read_tag_by_id(
|
|
405
|
-
self,
|
|
406
|
-
id: str,
|
|
407
|
-
include: list[str] | None = None,
|
|
408
|
-
fields: list[str] | None = None,
|
|
409
|
-
) -> dict[str, Any]: # Changed return type
|
|
337
|
+
async def read_tag_by_id(self, id: str, include: list[str] | None = None, fields: list[str] | None = None) -> dict[str, Any]:
|
|
410
338
|
"""
|
|
411
339
|
Retrieves a tag's details by its unique identifier, optionally filtering by included and field sets.
|
|
412
340
|
|
|
@@ -429,12 +357,7 @@ class GhostContentApp(APIApplication):
|
|
|
429
357
|
response = self._get(url, params=params)
|
|
430
358
|
return response.json()
|
|
431
359
|
|
|
432
|
-
def read_tag_by_slug(
|
|
433
|
-
self,
|
|
434
|
-
slug: str,
|
|
435
|
-
include: list[str] | None = None,
|
|
436
|
-
fields: list[str] | None = None,
|
|
437
|
-
) -> dict[str, Any]: # Changed return type
|
|
360
|
+
async def read_tag_by_slug(self, slug: str, include: list[str] | None = None, fields: list[str] | None = None) -> dict[str, Any]:
|
|
438
361
|
"""
|
|
439
362
|
Retrieve tag information identified by a unique slug, with optional inclusion of related data and selective fields.
|
|
440
363
|
|
|
@@ -457,8 +380,7 @@ class GhostContentApp(APIApplication):
|
|
|
457
380
|
response = self._get(url, params=params)
|
|
458
381
|
return response.json()
|
|
459
382
|
|
|
460
|
-
|
|
461
|
-
def browse_pages(
|
|
383
|
+
async def browse_pages(
|
|
462
384
|
self,
|
|
463
385
|
include: list[str] | None = None,
|
|
464
386
|
fields: list[str] | None = None,
|
|
@@ -467,7 +389,7 @@ class GhostContentApp(APIApplication):
|
|
|
467
389
|
page: int | None = None,
|
|
468
390
|
order: str | None = None,
|
|
469
391
|
formats: list[str] | None = None,
|
|
470
|
-
) -> dict[str, Any]:
|
|
392
|
+
) -> dict[str, Any]:
|
|
471
393
|
"""
|
|
472
394
|
Retrieves a list of pages using optional filtering, pagination, and formatting parameters.
|
|
473
395
|
|
|
@@ -490,26 +412,15 @@ class GhostContentApp(APIApplication):
|
|
|
490
412
|
browse, list, management, important
|
|
491
413
|
"""
|
|
492
414
|
url = f"{self.base_url}pages/"
|
|
493
|
-
# Removed 'visibility' from params as it's not in the method signature
|
|
494
415
|
params = self._build_common_params(
|
|
495
|
-
include=include,
|
|
496
|
-
fields=fields,
|
|
497
|
-
filter=filter,
|
|
498
|
-
limit=limit,
|
|
499
|
-
page=page,
|
|
500
|
-
order=order,
|
|
501
|
-
formats=formats,
|
|
416
|
+
include=include, fields=fields, filter=filter, limit=limit, page=page, order=order, formats=formats
|
|
502
417
|
)
|
|
503
418
|
response = self._get(url, params=params)
|
|
504
419
|
return response.json()
|
|
505
420
|
|
|
506
|
-
def read_page_by_id(
|
|
507
|
-
self,
|
|
508
|
-
|
|
509
|
-
include: list[str] | None = None,
|
|
510
|
-
fields: list[str] | None = None,
|
|
511
|
-
formats: list[str] | None = None,
|
|
512
|
-
) -> dict[str, Any]: # Changed return type
|
|
421
|
+
async def read_page_by_id(
|
|
422
|
+
self, id: str, include: list[str] | None = None, fields: list[str] | None = None, formats: list[str] | None = None
|
|
423
|
+
) -> dict[str, Any]:
|
|
513
424
|
"""
|
|
514
425
|
Read a page by ID, allowing for optional inclusion of additional data, specific fields, and formats.
|
|
515
426
|
|
|
@@ -529,19 +440,13 @@ class GhostContentApp(APIApplication):
|
|
|
529
440
|
read, page, data-retrieval
|
|
530
441
|
"""
|
|
531
442
|
url = f"{self.base_url}pages/{id}/"
|
|
532
|
-
params = self._build_common_params(
|
|
533
|
-
include=include, fields=fields, formats=formats
|
|
534
|
-
)
|
|
443
|
+
params = self._build_common_params(include=include, fields=fields, formats=formats)
|
|
535
444
|
response = self._get(url, params=params)
|
|
536
445
|
return response.json()
|
|
537
446
|
|
|
538
|
-
def read_page_by_slug(
|
|
539
|
-
self,
|
|
540
|
-
|
|
541
|
-
include: list[str] | None = None,
|
|
542
|
-
fields: list[str] | None = None,
|
|
543
|
-
formats: list[str] | None = None,
|
|
544
|
-
) -> dict[str, Any]: # Changed return type
|
|
447
|
+
async def read_page_by_slug(
|
|
448
|
+
self, slug: str, include: list[str] | None = None, fields: list[str] | None = None, formats: list[str] | None = None
|
|
449
|
+
) -> dict[str, Any]:
|
|
545
450
|
"""
|
|
546
451
|
Retrieve a page's content and metadata by its slug identifier, optionally including related data, specific fields, and content formats.
|
|
547
452
|
|
|
@@ -561,14 +466,11 @@ class GhostContentApp(APIApplication):
|
|
|
561
466
|
read, get, page, slug, http-request
|
|
562
467
|
"""
|
|
563
468
|
url = f"{self.base_url}pages/slug/{slug}/"
|
|
564
|
-
params = self._build_common_params(
|
|
565
|
-
include=include, fields=fields, formats=formats
|
|
566
|
-
)
|
|
469
|
+
params = self._build_common_params(include=include, fields=fields, formats=formats)
|
|
567
470
|
response = self._get(url, params=params)
|
|
568
471
|
return response.json()
|
|
569
472
|
|
|
570
|
-
|
|
571
|
-
def browse_tiers(
|
|
473
|
+
async def browse_tiers(
|
|
572
474
|
self,
|
|
573
475
|
include: list[str] | None = None,
|
|
574
476
|
fields: list[str] | None = None,
|
|
@@ -576,7 +478,7 @@ class GhostContentApp(APIApplication):
|
|
|
576
478
|
limit: int | None = None,
|
|
577
479
|
page: int | None = None,
|
|
578
480
|
order: str | None = None,
|
|
579
|
-
) -> dict[str, Any]:
|
|
481
|
+
) -> dict[str, Any]:
|
|
580
482
|
"""
|
|
581
483
|
Browse tiers based on optional filters and pagination.
|
|
582
484
|
|
|
@@ -598,20 +500,11 @@ class GhostContentApp(APIApplication):
|
|
|
598
500
|
browse, pagination, filter, management, important
|
|
599
501
|
"""
|
|
600
502
|
url = f"{self.base_url}tiers/"
|
|
601
|
-
|
|
602
|
-
params = self._build_common_params(
|
|
603
|
-
include=include,
|
|
604
|
-
fields=fields,
|
|
605
|
-
filter=filter,
|
|
606
|
-
limit=limit,
|
|
607
|
-
page=page,
|
|
608
|
-
order=order,
|
|
609
|
-
)
|
|
503
|
+
params = self._build_common_params(include=include, fields=fields, filter=filter, limit=limit, page=page, order=order)
|
|
610
504
|
response = self._get(url, params=params)
|
|
611
505
|
return response.json()
|
|
612
506
|
|
|
613
|
-
|
|
614
|
-
def browse_settings(self) -> dict[str, Any]: # Changed return type
|
|
507
|
+
async def browse_settings(self) -> dict[str, Any]:
|
|
615
508
|
"""
|
|
616
509
|
Fetches site settings by making a GET request to the settings endpoint.
|
|
617
510
|
|
|
@@ -628,9 +521,7 @@ class GhostContentApp(APIApplication):
|
|
|
628
521
|
fetch, settings, management, important
|
|
629
522
|
"""
|
|
630
523
|
url = f"{self.base_url}settings/"
|
|
631
|
-
params = (
|
|
632
|
-
self._build_common_params()
|
|
633
|
-
) # Only the API key is needed for this endpoint via _build_common_params
|
|
524
|
+
params = self._build_common_params()
|
|
634
525
|
response = self._get(url, params=params)
|
|
635
526
|
return response.json()
|
|
636
527
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from typing import Any
|
|
2
|
-
|
|
3
2
|
from loguru import logger
|
|
4
3
|
from universal_mcp.applications.application import APIApplication
|
|
5
4
|
from universal_mcp.integrations import Integration
|
|
@@ -17,12 +16,9 @@ class GithubApp(APIApplication):
|
|
|
17
16
|
credentials = self.integration.get_credentials()
|
|
18
17
|
if "headers" in credentials:
|
|
19
18
|
return credentials["headers"]
|
|
20
|
-
return {
|
|
21
|
-
"Authorization": f"Bearer {credentials['access_token']}",
|
|
22
|
-
"Accept": "application/vnd.github.v3+json",
|
|
23
|
-
}
|
|
19
|
+
return {"Authorization": f"Bearer {credentials['access_token']}", "Accept": "application/vnd.github.v3+json"}
|
|
24
20
|
|
|
25
|
-
def star_repository(self, repo_full_name: str) -> str:
|
|
21
|
+
async def star_repository(self, repo_full_name: str) -> str:
|
|
26
22
|
"""
|
|
27
23
|
Stars a GitHub repository for the authenticated user. This user-centric action takes the full repository name ('owner/repo') and returns a simple string message confirming the outcome, unlike other functions that list or create repository content like issues or pull requests.
|
|
28
24
|
|
|
@@ -49,7 +45,7 @@ class GithubApp(APIApplication):
|
|
|
49
45
|
logger.error(response.text)
|
|
50
46
|
return f"Error starring repository: {response.text}"
|
|
51
47
|
|
|
52
|
-
def list_recent_commits(self, repo_full_name: str) -> str:
|
|
48
|
+
async def list_recent_commits(self, repo_full_name: str) -> str:
|
|
53
49
|
"""
|
|
54
50
|
Fetches and formats the 12 most recent commits from a repository. It returns a human-readable string summarizing each commit's hash, author, and message, providing a focused overview of recent code changes, unlike functions that list branches, issues, or pull requests.
|
|
55
51
|
|
|
@@ -74,15 +70,14 @@ class GithubApp(APIApplication):
|
|
|
74
70
|
if not commits:
|
|
75
71
|
return f"No commits found for repository {repo_full_name}"
|
|
76
72
|
result = f"Recent commits for {repo_full_name}:\n\n"
|
|
77
|
-
for commit in commits[:12]:
|
|
73
|
+
for commit in commits[:12]:
|
|
78
74
|
sha = commit.get("sha", "")[:7]
|
|
79
75
|
message = commit.get("commit", {}).get("message", "").split("\n")[0]
|
|
80
76
|
author = commit.get("commit", {}).get("author", {}).get("name", "Unknown")
|
|
81
|
-
|
|
82
77
|
result += f"- {sha}: {message} (by {author})\n"
|
|
83
78
|
return result
|
|
84
79
|
|
|
85
|
-
def list_branches(self, repo_full_name: str) -> str:
|
|
80
|
+
async def list_branches(self, repo_full_name: str) -> str:
|
|
86
81
|
"""
|
|
87
82
|
Fetches all branches for a specified GitHub repository and formats them into a human-readable string. This method is distinct from others like `search_issues`, as it returns a formatted list for display rather than raw JSON data for programmatic use.
|
|
88
83
|
|
|
@@ -112,7 +107,7 @@ class GithubApp(APIApplication):
|
|
|
112
107
|
result += f"- {branch_name}\n"
|
|
113
108
|
return result
|
|
114
109
|
|
|
115
|
-
def list_pull_requests(self, repo_full_name: str, state: str = "open") -> str:
|
|
110
|
+
async def list_pull_requests(self, repo_full_name: str, state: str = "open") -> str:
|
|
116
111
|
"""
|
|
117
112
|
Fetches pull requests for a repository, filtered by state (e.g., 'open'). It returns a formatted string summarizing each PR's details, distinguishing it from `get_pull_request` (single PR) and `search_issues` (raw issue data).
|
|
118
113
|
|
|
@@ -143,20 +138,11 @@ class GithubApp(APIApplication):
|
|
|
143
138
|
pr_number = pr.get("number", "Unknown")
|
|
144
139
|
pr_state = pr.get("state", "Unknown")
|
|
145
140
|
pr_user = pr.get("user", {}).get("login", "Unknown")
|
|
146
|
-
|
|
147
|
-
result += (
|
|
148
|
-
f"- PR #{pr_number}: {pr_title} (by {pr_user}, Status: {pr_state})\n"
|
|
149
|
-
)
|
|
141
|
+
result += f"- PR #{pr_number}: {pr_title} (by {pr_user}, Status: {pr_state})\n"
|
|
150
142
|
return result
|
|
151
143
|
|
|
152
|
-
def search_issues(
|
|
153
|
-
self,
|
|
154
|
-
repo_full_name: str,
|
|
155
|
-
state: str = "open",
|
|
156
|
-
assignee: str = None,
|
|
157
|
-
labels: str = None,
|
|
158
|
-
per_page: int = 30,
|
|
159
|
-
page: int = 1,
|
|
144
|
+
async def search_issues(
|
|
145
|
+
self, repo_full_name: str, state: str = "open", assignee: str = None, labels: str = None, per_page: int = 30, page: int = 1
|
|
160
146
|
) -> list[dict[str, Any]]:
|
|
161
147
|
"""
|
|
162
148
|
Fetches issues from a GitHub repository using specified filters (state, assignee, labels) and pagination. It returns the raw API response as a list of dictionaries, providing detailed issue data for programmatic processing, distinct from other methods that return formatted strings.
|
|
@@ -190,7 +176,7 @@ class GithubApp(APIApplication):
|
|
|
190
176
|
response.raise_for_status()
|
|
191
177
|
return response.json()
|
|
192
178
|
|
|
193
|
-
def get_pull_request(self, repo_full_name: str, pull_number: int) -> str:
|
|
179
|
+
async def get_pull_request(self, repo_full_name: str, pull_number: int) -> str:
|
|
194
180
|
"""
|
|
195
181
|
Fetches a specific pull request from a repository using its unique number. It returns a human-readable string summarizing the PR's title, creator, status, and description, unlike `list_pull_requests` which retrieves a list of multiple PRs.
|
|
196
182
|
|
|
@@ -218,15 +204,10 @@ class GithubApp(APIApplication):
|
|
|
218
204
|
pr_state = pr.get("state", "Unknown")
|
|
219
205
|
pr_user = pr.get("user", {}).get("login", "Unknown")
|
|
220
206
|
pr_body = pr.get("body", "No description provided.")
|
|
221
|
-
result =
|
|
222
|
-
f"Pull Request #{pr_number}: {pr_title}\n"
|
|
223
|
-
f"Created by: {pr_user}\n"
|
|
224
|
-
f"Status: {pr_state}\n"
|
|
225
|
-
f"Description: {pr_body}\n"
|
|
226
|
-
)
|
|
207
|
+
result = f"Pull Request #{pr_number}: {pr_title}\nCreated by: {pr_user}\nStatus: {pr_state}\nDescription: {pr_body}\n"
|
|
227
208
|
return result
|
|
228
209
|
|
|
229
|
-
def create_pull_request(
|
|
210
|
+
async def create_pull_request(
|
|
230
211
|
self,
|
|
231
212
|
repo_full_name: str,
|
|
232
213
|
head: str,
|
|
@@ -262,12 +243,7 @@ class GithubApp(APIApplication):
|
|
|
262
243
|
"""
|
|
263
244
|
repo_full_name = repo_full_name.strip()
|
|
264
245
|
url = f"{self.base_api_url}/{repo_full_name}/pulls"
|
|
265
|
-
pull_request_data = {
|
|
266
|
-
"head": head,
|
|
267
|
-
"base": base,
|
|
268
|
-
"maintainer_can_modify": maintainer_can_modify,
|
|
269
|
-
"draft": draft,
|
|
270
|
-
}
|
|
246
|
+
pull_request_data = {"head": head, "base": base, "maintainer_can_modify": maintainer_can_modify, "draft": draft}
|
|
271
247
|
if issue is not None:
|
|
272
248
|
pull_request_data["issue"] = issue
|
|
273
249
|
else:
|
|
@@ -280,9 +256,7 @@ class GithubApp(APIApplication):
|
|
|
280
256
|
response.raise_for_status()
|
|
281
257
|
return response.json()
|
|
282
258
|
|
|
283
|
-
def create_issue(
|
|
284
|
-
self, repo_full_name: str, title: str, body: str = "", labels=None
|
|
285
|
-
) -> str:
|
|
259
|
+
async def create_issue(self, repo_full_name: str, title: str, body: str = "", labels=None) -> str:
|
|
286
260
|
"""
|
|
287
261
|
Creates a new issue in a GitHub repository using a title, body, and optional labels. It returns a formatted confirmation string with the new issue's number and URL, differing from `update_issue` which modifies existing issues and `search_issues` which returns raw API data.
|
|
288
262
|
|
|
@@ -306,9 +280,7 @@ class GithubApp(APIApplication):
|
|
|
306
280
|
issue_data = {"title": title, "body": body}
|
|
307
281
|
if labels:
|
|
308
282
|
if isinstance(labels, str):
|
|
309
|
-
labels_list = [
|
|
310
|
-
label.strip() for label in labels.split(",") if label.strip()
|
|
311
|
-
]
|
|
283
|
+
labels_list = [label.strip() for label in labels.split(",") if label.strip()]
|
|
312
284
|
issue_data["labels"] = labels_list
|
|
313
285
|
else:
|
|
314
286
|
issue_data["labels"] = labels
|
|
@@ -317,15 +289,9 @@ class GithubApp(APIApplication):
|
|
|
317
289
|
issue = response.json()
|
|
318
290
|
issue_number = issue.get("number", "Unknown")
|
|
319
291
|
issue_url = issue.get("html_url", "")
|
|
320
|
-
return
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
f"URL: {issue_url}"
|
|
324
|
-
)
|
|
325
|
-
|
|
326
|
-
def list_repo_activities(
|
|
327
|
-
self, repo_full_name: str, direction: str = "desc", per_page: int = 30
|
|
328
|
-
) -> str:
|
|
292
|
+
return f"Successfully created issue #{issue_number}:\nTitle: {title}\nURL: {issue_url}"
|
|
293
|
+
|
|
294
|
+
async def list_repo_activities(self, repo_full_name: str, direction: str = "desc", per_page: int = 30) -> str:
|
|
329
295
|
"""
|
|
330
296
|
Fetches recent events for a GitHub repository and formats them into a human-readable string. It summarizes activities with actors and timestamps, providing a general event feed, unlike other `list_*` functions which retrieve specific resources like commits or issues.
|
|
331
297
|
|
|
@@ -354,17 +320,14 @@ class GithubApp(APIApplication):
|
|
|
354
320
|
return f"No activities found for repository {repo_full_name}"
|
|
355
321
|
result = f"Repository activities for {repo_full_name}:\n\n"
|
|
356
322
|
for activity in activities:
|
|
357
|
-
# Extract common fields
|
|
358
323
|
timestamp = activity.get("timestamp", "Unknown time")
|
|
359
324
|
actor_name = "Unknown user"
|
|
360
325
|
if "actor" in activity and activity["actor"]:
|
|
361
326
|
actor_name = activity["actor"].get("login", "Unknown user")
|
|
362
|
-
|
|
363
|
-
# Create a simple description of the activity
|
|
364
327
|
result += f"- {actor_name} performed an activity at {timestamp}\n"
|
|
365
328
|
return result
|
|
366
329
|
|
|
367
|
-
def update_issue(
|
|
330
|
+
async def update_issue(
|
|
368
331
|
self,
|
|
369
332
|
repo_full_name: str,
|
|
370
333
|
issue_number: int,
|