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,546 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def parse_league_standings(data: dict[str, Any]) -> dict[str, Any]:
|
|
5
|
+
"""
|
|
6
|
+
Parse league standings data into a more usable format
|
|
7
|
+
|
|
8
|
+
Args:
|
|
9
|
+
data: Raw league data from the API
|
|
10
|
+
|
|
11
|
+
Returns:
|
|
12
|
+
Parsed league data
|
|
13
|
+
"""
|
|
14
|
+
# Handle error responses
|
|
15
|
+
if "error" in data:
|
|
16
|
+
return data
|
|
17
|
+
|
|
18
|
+
# Parse league info
|
|
19
|
+
league_info = {
|
|
20
|
+
"id": data.get("league", {}).get("id"),
|
|
21
|
+
"name": data.get("league", {}).get("name"),
|
|
22
|
+
"created": data.get("league", {}).get("created"),
|
|
23
|
+
"type": "Public"
|
|
24
|
+
if data.get("league", {}).get("league_type") == "s"
|
|
25
|
+
else "Private",
|
|
26
|
+
"scoring": "Classic"
|
|
27
|
+
if data.get("league", {}).get("scoring") == "c"
|
|
28
|
+
else "Head-to-Head",
|
|
29
|
+
"admin_entry": data.get("league", {}).get("admin_entry"),
|
|
30
|
+
"start_event": data.get("league", {}).get("start_event"),
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# Parse standings
|
|
34
|
+
standings = data.get("standings", {}).get("results", [])
|
|
35
|
+
|
|
36
|
+
# Get total count
|
|
37
|
+
total_count = len(standings)
|
|
38
|
+
|
|
39
|
+
# Format standings
|
|
40
|
+
formatted_standings = []
|
|
41
|
+
for standing in standings:
|
|
42
|
+
team = {
|
|
43
|
+
"id": standing.get("id"),
|
|
44
|
+
"team_id": standing.get("entry"),
|
|
45
|
+
"team_name": standing.get("entry_name"),
|
|
46
|
+
"manager_name": standing.get("player_name"),
|
|
47
|
+
"rank": standing.get("rank"),
|
|
48
|
+
"last_rank": standing.get("last_rank"),
|
|
49
|
+
"rank_change": standing.get("last_rank", 0) - standing.get("rank", 0)
|
|
50
|
+
if standing.get("last_rank") and standing.get("rank")
|
|
51
|
+
else 0,
|
|
52
|
+
"total_points": standing.get("total"),
|
|
53
|
+
"event_total": standing.get("event_total"),
|
|
54
|
+
}
|
|
55
|
+
formatted_standings.append(team)
|
|
56
|
+
|
|
57
|
+
response = {
|
|
58
|
+
"league_info": league_info,
|
|
59
|
+
"standings": formatted_standings[:25], # Limit to top 25 teams
|
|
60
|
+
"total_teams": total_count,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if len(formatted_standings) > 25:
|
|
64
|
+
response["disclaimers"] = ["Limited to top 25 teams"]
|
|
65
|
+
|
|
66
|
+
return response
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def get_teams_historical_data(
|
|
70
|
+
team_ids: list[int], api, start_gw: int | None = None, end_gw: int | None = None
|
|
71
|
+
) -> dict[str, Any]:
|
|
72
|
+
"""Get historical data for multiple teams"""
|
|
73
|
+
results = {}
|
|
74
|
+
errors = {}
|
|
75
|
+
|
|
76
|
+
# Validate and process gameweek range
|
|
77
|
+
try:
|
|
78
|
+
if end_gw is None or end_gw == "current":
|
|
79
|
+
current_gw_data = api.get_current_gameweek()
|
|
80
|
+
current_gw = current_gw_data.get("id", 38)
|
|
81
|
+
end_gw = current_gw
|
|
82
|
+
elif isinstance(end_gw, str) and end_gw.startswith("current-"):
|
|
83
|
+
current_gw_data = api.get_current_gameweek()
|
|
84
|
+
current_gw = current_gw_data.get("id", 38)
|
|
85
|
+
offset = int(end_gw.split("-")[1])
|
|
86
|
+
end_gw = max(1, current_gw - offset)
|
|
87
|
+
|
|
88
|
+
if start_gw is None:
|
|
89
|
+
start_gw = 1
|
|
90
|
+
elif isinstance(start_gw, str) and start_gw.startswith("current-"):
|
|
91
|
+
current_gw_data = api.get_current_gameweek()
|
|
92
|
+
current_gw = current_gw_data.get("id", 38)
|
|
93
|
+
offset = int(start_gw.split("-")[1])
|
|
94
|
+
start_gw = max(1, current_gw - offset)
|
|
95
|
+
|
|
96
|
+
start_gw = int(start_gw) if start_gw is not None else 1
|
|
97
|
+
end_gw = int(end_gw) if end_gw is not None else 38
|
|
98
|
+
|
|
99
|
+
start_gw = max(start_gw, 1)
|
|
100
|
+
end_gw = min(end_gw, 38)
|
|
101
|
+
if start_gw > end_gw:
|
|
102
|
+
start_gw, end_gw = end_gw, start_gw
|
|
103
|
+
|
|
104
|
+
except Exception as e:
|
|
105
|
+
return {
|
|
106
|
+
"error": f"Invalid gameweek range: {str(e)}",
|
|
107
|
+
"suggestion": "Use numeric values or 'current'/'current-N' format",
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# Get history data for each team
|
|
111
|
+
for team_id in team_ids:
|
|
112
|
+
try:
|
|
113
|
+
# Use the API class to make the request
|
|
114
|
+
history_data = api._make_request(f"entry/{team_id}/history/")
|
|
115
|
+
|
|
116
|
+
if "current" in history_data:
|
|
117
|
+
current = [
|
|
118
|
+
gw
|
|
119
|
+
for gw in history_data["current"]
|
|
120
|
+
if start_gw <= gw.get("event", 0) <= end_gw
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
filtered_data = {
|
|
124
|
+
"current": current,
|
|
125
|
+
"past": history_data.get("past", []),
|
|
126
|
+
"chips": history_data.get("chips", []),
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
results[team_id] = filtered_data
|
|
130
|
+
else:
|
|
131
|
+
errors[team_id] = "No historical data found"
|
|
132
|
+
|
|
133
|
+
except Exception as e:
|
|
134
|
+
errors[team_id] = str(e)
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
"teams_data": results,
|
|
138
|
+
"errors": errors,
|
|
139
|
+
"gameweek_range": {"start": start_gw, "end": end_gw},
|
|
140
|
+
"success_rate": len(results) / len(team_ids) if team_ids else 0,
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _get_league_standings(league_id: int, api) -> dict[str, Any]:
|
|
145
|
+
"""Get standings for a specified FPL league"""
|
|
146
|
+
try:
|
|
147
|
+
# Use the API class to make the request
|
|
148
|
+
data = api._make_request(f"leagues-classic/{league_id}/standings/")
|
|
149
|
+
except Exception as e:
|
|
150
|
+
return {"error": f"API request failed: {str(e)}"}
|
|
151
|
+
|
|
152
|
+
# Import parse_league_standings from the user's api.py or define a simple version here
|
|
153
|
+
# For now, we'll define a simple version
|
|
154
|
+
if "error" in data:
|
|
155
|
+
return data
|
|
156
|
+
|
|
157
|
+
league_info = {
|
|
158
|
+
"id": data.get("league", {}).get("id"),
|
|
159
|
+
"name": data.get("league", {}).get("name"),
|
|
160
|
+
"created": data.get("league", {}).get("created"),
|
|
161
|
+
"type": "Public"
|
|
162
|
+
if data.get("league", {}).get("league_type") == "s"
|
|
163
|
+
else "Private",
|
|
164
|
+
"scoring": "Classic"
|
|
165
|
+
if data.get("league", {}).get("scoring") == "c"
|
|
166
|
+
else "Head-to-Head",
|
|
167
|
+
"admin_entry": data.get("league", {}).get("admin_entry"),
|
|
168
|
+
"start_event": data.get("league", {}).get("start_event"),
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
standings = data.get("standings", {}).get("results", [])
|
|
172
|
+
total_count = len(standings)
|
|
173
|
+
|
|
174
|
+
formatted_standings = []
|
|
175
|
+
for standing in standings:
|
|
176
|
+
team = {
|
|
177
|
+
"id": standing.get("id"),
|
|
178
|
+
"team_id": standing.get("entry"),
|
|
179
|
+
"team_name": standing.get("entry_name"),
|
|
180
|
+
"manager_name": standing.get("player_name"),
|
|
181
|
+
"rank": standing.get("rank"),
|
|
182
|
+
"last_rank": standing.get("last_rank"),
|
|
183
|
+
"rank_change": standing.get("last_rank", 0) - standing.get("rank", 0)
|
|
184
|
+
if standing.get("last_rank") and standing.get("rank")
|
|
185
|
+
else 0,
|
|
186
|
+
"total_points": standing.get("total"),
|
|
187
|
+
"event_total": standing.get("event_total"),
|
|
188
|
+
}
|
|
189
|
+
formatted_standings.append(team)
|
|
190
|
+
|
|
191
|
+
response_data = {
|
|
192
|
+
"league_info": league_info,
|
|
193
|
+
"standings": formatted_standings[:25],
|
|
194
|
+
"total_teams": total_count,
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if len(formatted_standings) > 25:
|
|
198
|
+
response_data["disclaimers"] = ["Limited to top 25 teams"]
|
|
199
|
+
|
|
200
|
+
return response_data
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _get_league_historical_performance(
|
|
204
|
+
league_id: int,
|
|
205
|
+
api, # noqa: F821
|
|
206
|
+
start_gw: int | None = None,
|
|
207
|
+
end_gw: int | None = None,
|
|
208
|
+
) -> dict[str, Any]:
|
|
209
|
+
"""Get historical performance data for teams in a league"""
|
|
210
|
+
league_data = _get_league_standings(league_id, api)
|
|
211
|
+
|
|
212
|
+
if "error" in league_data:
|
|
213
|
+
return league_data
|
|
214
|
+
|
|
215
|
+
if "standings" in league_data and len(league_data["standings"]) > 25:
|
|
216
|
+
league_data["standings"] = league_data["standings"][:25]
|
|
217
|
+
league_data["limited_to_top"] = 25
|
|
218
|
+
|
|
219
|
+
team_ids = [team["team_id"] for team in league_data["standings"]]
|
|
220
|
+
|
|
221
|
+
historical_data = get_teams_historical_data(team_ids, api, start_gw, end_gw)
|
|
222
|
+
|
|
223
|
+
if "error" in historical_data:
|
|
224
|
+
return historical_data
|
|
225
|
+
|
|
226
|
+
teams_data = historical_data["teams_data"]
|
|
227
|
+
gameweek_range = historical_data["gameweek_range"]
|
|
228
|
+
|
|
229
|
+
gameweeks = list(range(gameweek_range["start"], gameweek_range["end"] + 1))
|
|
230
|
+
|
|
231
|
+
series = []
|
|
232
|
+
for team in league_data["standings"]:
|
|
233
|
+
team_id = team["team_id"]
|
|
234
|
+
|
|
235
|
+
if team_id not in teams_data:
|
|
236
|
+
continue
|
|
237
|
+
|
|
238
|
+
current = teams_data[team_id].get("current", [])
|
|
239
|
+
|
|
240
|
+
points_series = []
|
|
241
|
+
rank_series = []
|
|
242
|
+
value_series = []
|
|
243
|
+
|
|
244
|
+
for gw in gameweeks:
|
|
245
|
+
gw_data = next((g for g in current if g.get("event") == gw), None)
|
|
246
|
+
|
|
247
|
+
if gw_data:
|
|
248
|
+
points_series.append(gw_data.get("points", 0))
|
|
249
|
+
rank_series.append(gw_data.get("overall_rank", 0))
|
|
250
|
+
value_series.append(
|
|
251
|
+
gw_data.get("value", 0) / 10.0 if gw_data.get("value") else 0
|
|
252
|
+
)
|
|
253
|
+
else:
|
|
254
|
+
points_series.append(0)
|
|
255
|
+
rank_series.append(0)
|
|
256
|
+
value_series.append(0)
|
|
257
|
+
|
|
258
|
+
series.append(
|
|
259
|
+
{
|
|
260
|
+
"team_id": team_id,
|
|
261
|
+
"name": team["team_name"],
|
|
262
|
+
"manager": team["manager_name"],
|
|
263
|
+
"points_series": points_series,
|
|
264
|
+
"rank_series": rank_series,
|
|
265
|
+
"value_series": value_series,
|
|
266
|
+
"current_rank": team["rank"],
|
|
267
|
+
"total_points": team["total_points"],
|
|
268
|
+
}
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
gameweek_winners = {}
|
|
272
|
+
for gw_index, gw in enumerate(gameweeks):
|
|
273
|
+
max_points = 0
|
|
274
|
+
winner = None
|
|
275
|
+
|
|
276
|
+
for team in series:
|
|
277
|
+
points = team["points_series"][gw_index]
|
|
278
|
+
if points > max_points:
|
|
279
|
+
max_points = points
|
|
280
|
+
winner = {
|
|
281
|
+
"team_id": team["team_id"],
|
|
282
|
+
"name": team["name"],
|
|
283
|
+
"points": points,
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if winner:
|
|
287
|
+
gameweek_winners[str(gw)] = winner
|
|
288
|
+
|
|
289
|
+
for team in series:
|
|
290
|
+
rank_variance = 0
|
|
291
|
+
valid_ranks = [r for r in team["rank_series"] if r > 0]
|
|
292
|
+
|
|
293
|
+
if valid_ranks:
|
|
294
|
+
mean_rank = sum(valid_ranks) / len(valid_ranks)
|
|
295
|
+
rank_variance = sum((r - mean_rank) ** 2 for r in valid_ranks) / len(
|
|
296
|
+
valid_ranks
|
|
297
|
+
)
|
|
298
|
+
consistency_score = 10.0 - min(10.0, (rank_variance / 1000000) * 10)
|
|
299
|
+
team["consistency_score"] = round(consistency_score, 1)
|
|
300
|
+
else:
|
|
301
|
+
team["consistency_score"] = 0
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
"league_info": league_data["league_info"],
|
|
305
|
+
"gameweeks": gameweeks,
|
|
306
|
+
"teams": series,
|
|
307
|
+
"gameweek_winners": gameweek_winners,
|
|
308
|
+
"errors": historical_data["errors"],
|
|
309
|
+
"success_rate": historical_data["success_rate"],
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _get_league_team_composition(
|
|
314
|
+
league_id: int,
|
|
315
|
+
api, # noqa: F821
|
|
316
|
+
gameweek: int | None = None,
|
|
317
|
+
) -> dict[str, Any]:
|
|
318
|
+
"""Get team composition analysis for a league"""
|
|
319
|
+
league_data = _get_league_standings(league_id, api)
|
|
320
|
+
|
|
321
|
+
if "error" in league_data:
|
|
322
|
+
return league_data
|
|
323
|
+
|
|
324
|
+
if "standings" in league_data and len(league_data["standings"]) > 25:
|
|
325
|
+
league_data["standings"] = league_data["standings"][:25]
|
|
326
|
+
league_data["limited_to_top"] = 25
|
|
327
|
+
|
|
328
|
+
team_ids = [team["team_id"] for team in league_data["standings"]]
|
|
329
|
+
|
|
330
|
+
if gameweek is None:
|
|
331
|
+
current_gw_data = api.get_current_gameweek()
|
|
332
|
+
gameweek = current_gw_data.get("id", 1)
|
|
333
|
+
|
|
334
|
+
try:
|
|
335
|
+
gameweek = int(gameweek) if gameweek is not None else 1
|
|
336
|
+
except (ValueError, TypeError):
|
|
337
|
+
return {"error": f"Invalid gameweek value: {gameweek}"}
|
|
338
|
+
|
|
339
|
+
static_data = api.get_bootstrap_static()
|
|
340
|
+
all_players = static_data.get("elements", [])
|
|
341
|
+
|
|
342
|
+
teams_map = {t["id"]: t for t in static_data.get("teams", [])}
|
|
343
|
+
positions_map = {p["id"]: p for p in static_data.get("element_types", [])}
|
|
344
|
+
|
|
345
|
+
players_map = {}
|
|
346
|
+
for p in all_players:
|
|
347
|
+
player = dict(p)
|
|
348
|
+
|
|
349
|
+
team_id = player.get("team")
|
|
350
|
+
if team_id and team_id in teams_map:
|
|
351
|
+
player["team_short"] = teams_map[team_id].get("short_name")
|
|
352
|
+
|
|
353
|
+
position_id = player.get("element_type")
|
|
354
|
+
if position_id and position_id in positions_map:
|
|
355
|
+
player["position"] = positions_map[position_id].get("singular_name_short")
|
|
356
|
+
|
|
357
|
+
players_map[player["id"]] = player
|
|
358
|
+
|
|
359
|
+
teams_data = {}
|
|
360
|
+
errors = {}
|
|
361
|
+
|
|
362
|
+
for team_id in team_ids:
|
|
363
|
+
try:
|
|
364
|
+
# Use the API class to make the request
|
|
365
|
+
picks_data = api._make_request(f"entry/{team_id}/event/{gameweek}/picks/")
|
|
366
|
+
teams_data[team_id] = picks_data
|
|
367
|
+
|
|
368
|
+
except Exception as e:
|
|
369
|
+
errors[team_id] = str(e)
|
|
370
|
+
|
|
371
|
+
if not teams_data:
|
|
372
|
+
return {
|
|
373
|
+
"error": "Failed to retrieve team data for any teams in the league",
|
|
374
|
+
"errors": errors,
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
player_ownership = {}
|
|
378
|
+
captain_picks = {}
|
|
379
|
+
vice_captain_picks = {}
|
|
380
|
+
position_distribution = {"GKP": {}, "DEF": {}, "MID": {}, "FWD": {}}
|
|
381
|
+
team_values = {}
|
|
382
|
+
|
|
383
|
+
for team_id, team_data in teams_data.items():
|
|
384
|
+
team_info = next(
|
|
385
|
+
(t for t in league_data["standings"] if t["team_id"] == team_id), None
|
|
386
|
+
)
|
|
387
|
+
if not team_info:
|
|
388
|
+
continue
|
|
389
|
+
|
|
390
|
+
picks = team_data.get("picks", [])
|
|
391
|
+
entry_history = team_data.get("entry_history", {})
|
|
392
|
+
|
|
393
|
+
team_values[team_id] = {
|
|
394
|
+
"team_name": team_info["team_name"],
|
|
395
|
+
"manager_name": team_info["manager_name"],
|
|
396
|
+
"bank": entry_history.get("bank", 0) / 10.0 if entry_history else 0,
|
|
397
|
+
"value": entry_history.get("value", 0) / 10.0 if entry_history else 0,
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
for pick in picks:
|
|
401
|
+
player_id = pick.get("element")
|
|
402
|
+
if not player_id:
|
|
403
|
+
continue
|
|
404
|
+
|
|
405
|
+
player_data = players_map.get(player_id, {})
|
|
406
|
+
if not player_data:
|
|
407
|
+
continue
|
|
408
|
+
|
|
409
|
+
if player_id not in player_ownership:
|
|
410
|
+
position = player_data.get("position", "UNK")
|
|
411
|
+
full_name = f"{player_data.get('first_name', '')} {player_data.get('second_name', '')}"
|
|
412
|
+
player_ownership[player_id] = {
|
|
413
|
+
"id": player_id,
|
|
414
|
+
"name": player_data.get("web_name", "Unknown"),
|
|
415
|
+
"full_name": full_name.strip() or "Unknown",
|
|
416
|
+
"position": position,
|
|
417
|
+
"team": player_data.get("team_short", "UNK"),
|
|
418
|
+
"price": player_data.get("now_cost", 0) / 10.0
|
|
419
|
+
if player_data.get("now_cost")
|
|
420
|
+
else 0,
|
|
421
|
+
"form": float(player_data.get("form", "0.0"))
|
|
422
|
+
if player_data.get("form")
|
|
423
|
+
else 0,
|
|
424
|
+
"total_points": player_data.get("total_points", 0),
|
|
425
|
+
"points_per_game": float(player_data.get("points_per_game", "0.0"))
|
|
426
|
+
if player_data.get("points_per_game")
|
|
427
|
+
else 0,
|
|
428
|
+
"ownership_count": 0,
|
|
429
|
+
"ownership_percent": 0,
|
|
430
|
+
"captain_count": 0,
|
|
431
|
+
"vice_captain_count": 0,
|
|
432
|
+
"teams": [],
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
player_ownership[player_id]["ownership_count"] += 1
|
|
436
|
+
player_ownership[player_id]["teams"].append(
|
|
437
|
+
{
|
|
438
|
+
"team_id": team_id,
|
|
439
|
+
"team_name": team_info["team_name"],
|
|
440
|
+
"manager_name": team_info["manager_name"],
|
|
441
|
+
"is_captain": pick.get("is_captain", False),
|
|
442
|
+
"is_vice_captain": pick.get("is_vice_captain", False),
|
|
443
|
+
"multiplier": pick.get("multiplier", 0),
|
|
444
|
+
"position": pick.get("position", 0),
|
|
445
|
+
}
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
if pick.get("is_captain", False):
|
|
449
|
+
if player_id not in captain_picks:
|
|
450
|
+
captain_picks[player_id] = 0
|
|
451
|
+
captain_picks[player_id] += 1
|
|
452
|
+
player_ownership[player_id]["captain_count"] += 1
|
|
453
|
+
|
|
454
|
+
if pick.get("is_vice_captain", False):
|
|
455
|
+
if player_id not in vice_captain_picks:
|
|
456
|
+
vice_captain_picks[player_id] = 0
|
|
457
|
+
vice_captain_picks[player_id] += 1
|
|
458
|
+
player_ownership[player_id]["vice_captain_count"] += 1
|
|
459
|
+
|
|
460
|
+
position = player_data.get("position", "UNK")
|
|
461
|
+
if position in position_distribution:
|
|
462
|
+
if player_id not in position_distribution[position]:
|
|
463
|
+
position_distribution[position][player_id] = 0
|
|
464
|
+
position_distribution[position][player_id] += 1
|
|
465
|
+
|
|
466
|
+
team_count = len(teams_data)
|
|
467
|
+
for player_id, player in player_ownership.items():
|
|
468
|
+
player["ownership_percent"] = round(
|
|
469
|
+
(player["ownership_count"] / team_count) * 100, 1
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
players_by_ownership = sorted(
|
|
473
|
+
player_ownership.values(),
|
|
474
|
+
key=lambda p: (p["ownership_percent"], p["total_points"]),
|
|
475
|
+
reverse=True,
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
template_threshold = 30.0
|
|
479
|
+
differential_threshold = 10.0
|
|
480
|
+
|
|
481
|
+
template_players = [
|
|
482
|
+
p for p in players_by_ownership if p["ownership_percent"] > template_threshold
|
|
483
|
+
]
|
|
484
|
+
differential_players = [
|
|
485
|
+
p
|
|
486
|
+
for p in players_by_ownership
|
|
487
|
+
if 0 < p["ownership_percent"] < differential_threshold
|
|
488
|
+
]
|
|
489
|
+
|
|
490
|
+
position_order = {"GKP": 1, "DEF": 2, "MID": 3, "FWD": 4, "UNK": 5}
|
|
491
|
+
template_players.sort(
|
|
492
|
+
key=lambda p: (position_order.get(p["position"], 5), -p["ownership_percent"])
|
|
493
|
+
)
|
|
494
|
+
differential_players.sort(
|
|
495
|
+
key=lambda p: (position_order.get(p["position"], 5), -p["total_points"])
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
captain_data = []
|
|
499
|
+
for player_id, count in sorted(
|
|
500
|
+
captain_picks.items(), key=lambda x: x[1], reverse=True
|
|
501
|
+
):
|
|
502
|
+
if player_id in player_ownership:
|
|
503
|
+
player = player_ownership[player_id]
|
|
504
|
+
captain_data.append(
|
|
505
|
+
{
|
|
506
|
+
"id": player_id,
|
|
507
|
+
"name": player["name"],
|
|
508
|
+
"count": count,
|
|
509
|
+
"percent": round((count / team_count) * 100, 1),
|
|
510
|
+
"position": player["position"],
|
|
511
|
+
"team": player["team"],
|
|
512
|
+
"points": player["total_points"],
|
|
513
|
+
}
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
value_stats = {}
|
|
517
|
+
for team_id, value_data in team_values.items():
|
|
518
|
+
value_stats[team_id] = {
|
|
519
|
+
"team_name": value_data["team_name"],
|
|
520
|
+
"manager_name": value_data["manager_name"],
|
|
521
|
+
"bank": value_data["bank"],
|
|
522
|
+
"team_value": value_data["value"],
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
sorted_value_stats = sorted(
|
|
526
|
+
value_stats.values(), key=lambda v: v["team_value"], reverse=True
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
PLAYER_LIMIT = 25
|
|
530
|
+
|
|
531
|
+
return {
|
|
532
|
+
"league_info": league_data["league_info"],
|
|
533
|
+
"gameweek": gameweek,
|
|
534
|
+
"teams_analyzed": team_count,
|
|
535
|
+
"player_ownership": {
|
|
536
|
+
"all_players": players_by_ownership[:PLAYER_LIMIT],
|
|
537
|
+
"template_players": template_players[:PLAYER_LIMIT],
|
|
538
|
+
"differential_players": differential_players[:PLAYER_LIMIT],
|
|
539
|
+
"captain_picks": captain_data[:PLAYER_LIMIT]
|
|
540
|
+
if len(captain_data) > PLAYER_LIMIT
|
|
541
|
+
else captain_data,
|
|
542
|
+
},
|
|
543
|
+
"team_values": sorted_value_stats,
|
|
544
|
+
"errors": errors,
|
|
545
|
+
"success_rate": len(teams_data) / len(team_ids) if team_ids else 0,
|
|
546
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Utilities for normalizing position terms in FPL context."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# Comprehensive position mapping dictionary
|
|
5
|
+
POSITION_MAPPINGS = {
|
|
6
|
+
# Standard FPL codes
|
|
7
|
+
"GKP": "GKP",
|
|
8
|
+
"DEF": "DEF",
|
|
9
|
+
"MID": "MID",
|
|
10
|
+
"FWD": "FWD",
|
|
11
|
+
# Common variations - singular
|
|
12
|
+
"goalkeeper": "GKP",
|
|
13
|
+
"goalie": "GKP",
|
|
14
|
+
"keeper": "GKP",
|
|
15
|
+
"defender": "DEF",
|
|
16
|
+
"fullback": "DEF",
|
|
17
|
+
"center-back": "DEF",
|
|
18
|
+
"cb": "DEF",
|
|
19
|
+
"midfielder": "MID",
|
|
20
|
+
"mid": "MID",
|
|
21
|
+
"winger": "MID",
|
|
22
|
+
"forward": "FWD",
|
|
23
|
+
"striker": "FWD",
|
|
24
|
+
"attacker": "FWD",
|
|
25
|
+
"st": "FWD",
|
|
26
|
+
# Common variations - plural
|
|
27
|
+
"goalkeepers": "GKP",
|
|
28
|
+
"goalies": "GKP",
|
|
29
|
+
"keepers": "GKP",
|
|
30
|
+
"defenders": "DEF",
|
|
31
|
+
"fullbacks": "DEF",
|
|
32
|
+
"center-backs": "DEF",
|
|
33
|
+
"midfielders": "MID",
|
|
34
|
+
"mids": "MID",
|
|
35
|
+
"wingers": "MID",
|
|
36
|
+
"forwards": "FWD",
|
|
37
|
+
"strikers": "FWD",
|
|
38
|
+
"attackers": "FWD",
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def normalize_position(position_term: str | None) -> str | None:
|
|
43
|
+
"""Convert various position terms to standard FPL position codes.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
position_term: Position term to normalize (can be None)
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Normalized FPL position code or None if input is None
|
|
50
|
+
"""
|
|
51
|
+
if not position_term:
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
# Convert to lowercase for case-insensitive matching
|
|
55
|
+
normalized = position_term.lower().strip()
|
|
56
|
+
|
|
57
|
+
# Try direct match in mapping (case insensitive)
|
|
58
|
+
for term, code in POSITION_MAPPINGS.items():
|
|
59
|
+
if normalized == term.lower():
|
|
60
|
+
return code
|
|
61
|
+
|
|
62
|
+
# Try partial matches
|
|
63
|
+
for term, code in POSITION_MAPPINGS.items():
|
|
64
|
+
if normalized in term.lower() or term.lower() in normalized:
|
|
65
|
+
return code
|
|
66
|
+
|
|
67
|
+
# No match found, return original
|
|
68
|
+
return position_term
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# GhostContentApp MCP Server
|
|
2
|
+
|
|
3
|
+
An MCP Server for the GhostContentApp API.
|
|
4
|
+
|
|
5
|
+
## 🛠️ Tool List
|
|
6
|
+
|
|
7
|
+
This is automatically generated from OpenAPI schema for the GhostContentApp API.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
| Tool | Description |
|
|
11
|
+
|------|-------------|
|
|
12
|
+
| `browse_posts` | Retrieves and browses posts from a data source based on provided parameters. |
|
|
13
|
+
| `read_post_by_id` | Retrieves a post by its ID, optionally including additional data or specific fields. |
|
|
14
|
+
| `read_post_by_slug` | Retrieves a post by its slug, with optional parameters to specify included data, select specific fields, or request particular data formats. |
|
|
15
|
+
| `browse_authors` | Browse authors using various filtering and pagination options. |
|
|
16
|
+
| `read_author_by_id` | Read an author from the database by their unique ID. |
|
|
17
|
+
| `read_author_by_slug` | Retrieve an author's information by their slug. |
|
|
18
|
+
| `browse_tags` | Browse and retrieve tags based on specified parameters. |
|
|
19
|
+
| `read_tag_by_id` | Retrieves a tag's details by its unique identifier, optionally filtering by included and field sets. |
|
|
20
|
+
| `read_tag_by_slug` | Retrieve tag information identified by a unique slug, with optional inclusion of related data and selective fields. |
|
|
21
|
+
| `browse_pages` | Retrieves a list of pages using optional filtering, pagination, and formatting parameters. |
|
|
22
|
+
| `read_page_by_id` | Read a page by ID, allowing for optional inclusion of additional data, specific fields, and formats. |
|
|
23
|
+
| `read_page_by_slug` | Retrieve a page's content and metadata by its slug identifier, optionally including related data, specific fields, and content formats. |
|
|
24
|
+
| `browse_tiers` | Browse tiers based on optional filters and pagination. |
|
|
25
|
+
| `browse_settings` | Fetches site settings by making a GET request to the settings endpoint. |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .app import GhostContentApp
|