universal-mcp-applications 0.1.17__py3-none-any.whl → 0.1.33__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of universal-mcp-applications might be problematic. Click here for more details.
- universal_mcp/applications/BEST_PRACTICES.md +166 -0
- universal_mcp/applications/ahrefs/README.md +3 -3
- universal_mcp/applications/airtable/README.md +3 -3
- universal_mcp/applications/airtable/app.py +0 -1
- universal_mcp/applications/apollo/app.py +0 -1
- universal_mcp/applications/asana/README.md +3 -3
- universal_mcp/applications/aws_s3/README.md +29 -0
- universal_mcp/applications/aws_s3/app.py +40 -39
- universal_mcp/applications/bill/README.md +249 -0
- universal_mcp/applications/browser_use/README.md +1 -0
- universal_mcp/applications/browser_use/__init__.py +0 -0
- universal_mcp/applications/browser_use/app.py +71 -0
- universal_mcp/applications/calendly/README.md +45 -45
- universal_mcp/applications/calendly/app.py +125 -125
- universal_mcp/applications/canva/README.md +35 -35
- universal_mcp/applications/canva/app.py +95 -99
- universal_mcp/applications/clickup/README.md +4 -4
- universal_mcp/applications/confluence/app.py +0 -1
- universal_mcp/applications/contentful/README.md +1 -2
- universal_mcp/applications/contentful/app.py +4 -5
- universal_mcp/applications/crustdata/README.md +3 -3
- universal_mcp/applications/domain_checker/README.md +2 -2
- universal_mcp/applications/domain_checker/app.py +11 -15
- universal_mcp/applications/e2b/README.md +4 -4
- universal_mcp/applications/e2b/app.py +4 -4
- universal_mcp/applications/elevenlabs/README.md +3 -77
- universal_mcp/applications/elevenlabs/app.py +18 -15
- universal_mcp/applications/exa/README.md +7 -7
- universal_mcp/applications/exa/app.py +17 -17
- universal_mcp/applications/falai/README.md +13 -12
- universal_mcp/applications/falai/app.py +34 -35
- universal_mcp/applications/figma/README.md +3 -3
- universal_mcp/applications/file_system/README.md +13 -0
- universal_mcp/applications/file_system/app.py +9 -9
- universal_mcp/applications/firecrawl/README.md +9 -9
- universal_mcp/applications/firecrawl/app.py +46 -46
- universal_mcp/applications/fireflies/README.md +14 -14
- universal_mcp/applications/fireflies/app.py +164 -57
- universal_mcp/applications/fpl/README.md +12 -12
- universal_mcp/applications/fpl/app.py +54 -55
- universal_mcp/applications/ghost_content/app.py +0 -1
- universal_mcp/applications/github/README.md +10 -10
- universal_mcp/applications/github/app.py +50 -52
- universal_mcp/applications/google_calendar/README.md +10 -10
- universal_mcp/applications/google_calendar/app.py +50 -49
- universal_mcp/applications/google_docs/README.md +14 -14
- universal_mcp/applications/google_docs/app.py +307 -233
- universal_mcp/applications/google_drive/README.md +54 -57
- universal_mcp/applications/google_drive/app.py +270 -261
- universal_mcp/applications/google_gemini/README.md +3 -14
- universal_mcp/applications/google_gemini/app.py +15 -18
- universal_mcp/applications/google_mail/README.md +20 -20
- universal_mcp/applications/google_mail/app.py +110 -109
- universal_mcp/applications/google_searchconsole/README.md +10 -10
- universal_mcp/applications/google_searchconsole/app.py +37 -37
- universal_mcp/applications/google_sheet/README.md +25 -25
- universal_mcp/applications/google_sheet/app.py +270 -266
- universal_mcp/applications/hashnode/README.md +6 -3
- universal_mcp/applications/hashnode/app.py +174 -25
- universal_mcp/applications/http_tools/README.md +5 -5
- universal_mcp/applications/http_tools/app.py +10 -11
- universal_mcp/applications/hubspot/api_segments/__init__.py +0 -0
- universal_mcp/applications/hubspot/api_segments/api_segment_base.py +54 -0
- universal_mcp/applications/hubspot/api_segments/crm_api.py +7337 -0
- universal_mcp/applications/hubspot/api_segments/marketing_api.py +1467 -0
- universal_mcp/applications/hubspot/app.py +2 -15
- universal_mcp/applications/jira/app.py +0 -1
- universal_mcp/applications/klaviyo/README.md +0 -36
- universal_mcp/applications/linkedin/README.md +18 -4
- universal_mcp/applications/linkedin/app.py +763 -162
- universal_mcp/applications/mailchimp/README.md +3 -3
- universal_mcp/applications/markitdown/app.py +10 -5
- universal_mcp/applications/ms_teams/README.md +31 -31
- universal_mcp/applications/ms_teams/app.py +151 -151
- universal_mcp/applications/neon/README.md +3 -3
- universal_mcp/applications/onedrive/README.md +24 -0
- universal_mcp/applications/onedrive/__init__.py +1 -0
- universal_mcp/applications/onedrive/app.py +338 -0
- universal_mcp/applications/openai/README.md +18 -17
- universal_mcp/applications/openai/app.py +40 -39
- universal_mcp/applications/outlook/README.md +9 -9
- universal_mcp/applications/outlook/app.py +307 -225
- universal_mcp/applications/perplexity/README.md +4 -4
- universal_mcp/applications/perplexity/app.py +4 -4
- universal_mcp/applications/posthog/README.md +128 -127
- universal_mcp/applications/reddit/README.md +21 -124
- universal_mcp/applications/reddit/app.py +51 -68
- universal_mcp/applications/resend/README.md +29 -29
- universal_mcp/applications/resend/app.py +116 -117
- universal_mcp/applications/rocketlane/app.py +0 -1
- universal_mcp/applications/scraper/README.md +7 -4
- universal_mcp/applications/scraper/__init__.py +1 -1
- universal_mcp/applications/scraper/app.py +341 -103
- universal_mcp/applications/semrush/README.md +3 -0
- universal_mcp/applications/serpapi/README.md +3 -3
- universal_mcp/applications/serpapi/app.py +14 -14
- universal_mcp/applications/sharepoint/README.md +19 -0
- universal_mcp/applications/sharepoint/app.py +285 -173
- universal_mcp/applications/shopify/app.py +0 -1
- universal_mcp/applications/shortcut/README.md +3 -3
- universal_mcp/applications/slack/README.md +23 -0
- universal_mcp/applications/slack/app.py +79 -48
- universal_mcp/applications/spotify/README.md +3 -3
- universal_mcp/applications/supabase/README.md +3 -3
- universal_mcp/applications/tavily/README.md +4 -4
- universal_mcp/applications/tavily/app.py +4 -4
- universal_mcp/applications/twilio/README.md +15 -0
- universal_mcp/applications/twitter/README.md +92 -89
- universal_mcp/applications/twitter/api_segments/compliance_api.py +13 -15
- universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +20 -20
- universal_mcp/applications/twitter/api_segments/dm_events_api.py +12 -12
- universal_mcp/applications/twitter/api_segments/likes_api.py +12 -12
- universal_mcp/applications/twitter/api_segments/lists_api.py +37 -39
- universal_mcp/applications/twitter/api_segments/spaces_api.py +24 -24
- universal_mcp/applications/twitter/api_segments/trends_api.py +4 -4
- universal_mcp/applications/twitter/api_segments/tweets_api.py +105 -105
- universal_mcp/applications/twitter/api_segments/usage_api.py +4 -4
- universal_mcp/applications/twitter/api_segments/users_api.py +136 -136
- universal_mcp/applications/twitter/app.py +15 -11
- universal_mcp/applications/whatsapp/README.md +12 -12
- universal_mcp/applications/whatsapp/app.py +66 -67
- universal_mcp/applications/whatsapp/audio.py +39 -35
- universal_mcp/applications/whatsapp/whatsapp.py +176 -154
- universal_mcp/applications/whatsapp_business/README.md +23 -23
- universal_mcp/applications/whatsapp_business/app.py +92 -92
- universal_mcp/applications/yahoo_finance/README.md +17 -0
- universal_mcp/applications/yahoo_finance/__init__.py +1 -0
- universal_mcp/applications/yahoo_finance/app.py +300 -0
- universal_mcp/applications/youtube/README.md +46 -46
- universal_mcp/applications/youtube/app.py +208 -195
- universal_mcp/applications/zenquotes/README.md +1 -1
- universal_mcp/applications/zenquotes/__init__.py +2 -0
- universal_mcp/applications/zenquotes/app.py +5 -5
- {universal_mcp_applications-0.1.17.dist-info → universal_mcp_applications-0.1.33.dist-info}/METADATA +5 -90
- {universal_mcp_applications-0.1.17.dist-info → universal_mcp_applications-0.1.33.dist-info}/RECORD +137 -128
- universal_mcp/applications/replicate/README.md +0 -18
- universal_mcp/applications/replicate/__init__.py +0 -1
- universal_mcp/applications/replicate/app.py +0 -493
- universal_mcp/applications/unipile/README.md +0 -28
- universal_mcp/applications/unipile/__init__.py +0 -1
- universal_mcp/applications/unipile/app.py +0 -827
- {universal_mcp_applications-0.1.17.dist-info → universal_mcp_applications-0.1.33.dist-info}/WHEEL +0 -0
- {universal_mcp_applications-0.1.17.dist-info → universal_mcp_applications-0.1.33.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,226 +1,338 @@
|
|
|
1
1
|
import base64
|
|
2
|
-
import
|
|
3
|
-
from datetime import datetime
|
|
4
|
-
from io import BytesIO
|
|
2
|
+
import os
|
|
5
3
|
from typing import Any
|
|
6
4
|
|
|
7
5
|
from loguru import logger
|
|
8
|
-
from
|
|
9
|
-
|
|
10
|
-
from universal_mcp.applications.application import BaseApplication
|
|
6
|
+
from universal_mcp.applications.application import APIApplication
|
|
11
7
|
from universal_mcp.integrations import Integration
|
|
12
8
|
|
|
13
9
|
|
|
14
|
-
|
|
15
|
-
"""Converts a datetime object to ISO format string, or returns None if the object is None."""
|
|
16
|
-
if dt_obj is not None:
|
|
17
|
-
return dt_obj.isoformat()
|
|
18
|
-
return None
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class SharepointApp(BaseApplication):
|
|
10
|
+
class SharepointApp(APIApplication):
|
|
22
11
|
"""
|
|
23
|
-
|
|
12
|
+
Application for interacting with Microsoft SharePoint API (via Microsoft Graph).
|
|
13
|
+
Provides tools to manage files, folders, and access Drive information.
|
|
24
14
|
"""
|
|
25
15
|
|
|
26
|
-
def __init__(self, integration: Integration
|
|
27
|
-
""
|
|
16
|
+
def __init__(self, integration: Integration | None = None, **kwargs) -> None:
|
|
17
|
+
super().__init__(name="sharepoint", integration=integration, **kwargs)
|
|
18
|
+
self.base_url = "https://graph.microsoft.com/v1.0"
|
|
19
|
+
|
|
20
|
+
def get_my_profile(self) -> dict[str, Any]:
|
|
21
|
+
"""
|
|
22
|
+
Fetches the profile for the currently authenticated user, specifically retrieving their ID and user principal name. This function confirms user identity, distinguishing it from `get_drive_info`, which returns details about the SharePoint storage space (e.g., quota) rather than the user's personal profile.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
dict[str, Any]: A dictionary containing the user's id and userPrincipalName.
|
|
26
|
+
|
|
27
|
+
Raises:
|
|
28
|
+
HTTPStatusError: If the API request fails.
|
|
29
|
+
|
|
30
|
+
Tags:
|
|
31
|
+
profile, user, account
|
|
32
|
+
"""
|
|
33
|
+
url = f"{self.base_url}/me"
|
|
34
|
+
query_params = {"$select": "id,userPrincipalName"}
|
|
35
|
+
response = self._get(url, params=query_params)
|
|
36
|
+
return self._handle_response(response)
|
|
37
|
+
|
|
38
|
+
def get_drive_info(self) -> dict[str, Any]:
|
|
39
|
+
"""
|
|
40
|
+
Fetches high-level information about the user's entire SharePoint. It returns drive-wide details like the owner and storage quota, differing from `get_item_metadata` which describes a specific item, and `get_my_profile` which retrieves general user account information.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
A dictionary containing drive information.
|
|
44
|
+
|
|
45
|
+
Tags:
|
|
46
|
+
drive, storage, quota, info
|
|
47
|
+
"""
|
|
48
|
+
url = f"{self.base_url}/me/drive"
|
|
49
|
+
response = self._get(url)
|
|
50
|
+
return self._handle_response(response)
|
|
51
|
+
|
|
52
|
+
def _list_drive_items(self, item_id: str = "root") -> dict[str, Any]:
|
|
53
|
+
"""
|
|
54
|
+
Lists the files and folders in the current user's SharePoint.
|
|
55
|
+
|
|
28
56
|
Args:
|
|
29
|
-
|
|
57
|
+
item_id (str, optional): The ID of the folder to list. Defaults to 'root'.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
A dictionary containing the list of files and folders.
|
|
30
61
|
"""
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
self.
|
|
34
|
-
self._site_url = None
|
|
62
|
+
url = f"{self.base_url}/me/drive/items/{item_id}/children"
|
|
63
|
+
response = self._get(url)
|
|
64
|
+
return self._handle_response(response)
|
|
35
65
|
|
|
36
|
-
|
|
37
|
-
def client(self):
|
|
66
|
+
def search_files(self, query: str) -> dict[str, Any]:
|
|
38
67
|
"""
|
|
39
|
-
|
|
40
|
-
|
|
68
|
+
Searches the user's entire SharePoint for files and folders matching a specified text query. This function performs a comprehensive search from the drive's root, distinguishing it from `list_files` or `list_folders` which only browse the contents of a single directory.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
query (str): The search query.
|
|
72
|
+
|
|
41
73
|
Returns:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if not
|
|
48
|
-
raise ValueError("
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
refresh_token = credentials.get("refresh_token")
|
|
59
|
-
return {
|
|
60
|
-
"access_token": access_token,
|
|
61
|
-
"refresh_token": refresh_token,
|
|
62
|
-
"token_type": "Bearer",
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if self._client is None:
|
|
66
|
-
self._client = GraphClient(token_callback=_acquire_token)
|
|
67
|
-
# Get me
|
|
68
|
-
me = self._client.me.get().execute_query()
|
|
69
|
-
logger.debug(me.properties)
|
|
70
|
-
# Get sites
|
|
71
|
-
sites = self._client.sites.root.get().execute_query()
|
|
72
|
-
self._site_url = sites.properties.get("id")
|
|
73
|
-
return self._client
|
|
74
|
-
|
|
75
|
-
def list_folders(self, folder_path: str | None = None) -> list[dict[str, Any]]:
|
|
76
|
-
"""
|
|
77
|
-
Retrieves the names of all immediate subfolders within a specified directory. If a path is not provided, it defaults to listing folders in the root of the user's drive. This function is distinct from `list_documents`, which lists files.
|
|
78
|
-
|
|
74
|
+
A dictionary containing the search results.
|
|
75
|
+
|
|
76
|
+
Tags:
|
|
77
|
+
search, find, query, files, important
|
|
78
|
+
"""
|
|
79
|
+
if not query:
|
|
80
|
+
raise ValueError("Search query cannot be empty.")
|
|
81
|
+
|
|
82
|
+
url = f"{self.base_url}/me/drive/root/search(q='{query}')"
|
|
83
|
+
response = self._get(url)
|
|
84
|
+
return self._handle_response(response)
|
|
85
|
+
|
|
86
|
+
def get_item_metadata(self, item_id: str) -> dict[str, Any]:
|
|
87
|
+
"""
|
|
88
|
+
Fetches detailed metadata for a specific file or folder using its unique ID. It returns properties like name, size, and type. Unlike `get_document_content`, it doesn't retrieve the file's actual content, focusing solely on the item's attributes for quick inspection without a full download.
|
|
89
|
+
|
|
79
90
|
Args:
|
|
80
|
-
|
|
81
|
-
|
|
91
|
+
item_id (str): The ID of the file or folder.
|
|
92
|
+
|
|
82
93
|
Returns:
|
|
83
|
-
|
|
84
|
-
|
|
94
|
+
A dictionary containing the item's metadata.
|
|
95
|
+
|
|
85
96
|
Tags:
|
|
86
|
-
|
|
97
|
+
metadata, info, file, folder
|
|
87
98
|
"""
|
|
88
|
-
if
|
|
89
|
-
|
|
90
|
-
folders = folder.get_folders(False).execute_query()
|
|
91
|
-
else:
|
|
92
|
-
folders = self.client.me.drive.root.get_folders(False).execute_query()
|
|
99
|
+
if not item_id:
|
|
100
|
+
raise ValueError("Missing required parameter 'item_id'.")
|
|
93
101
|
|
|
94
|
-
|
|
102
|
+
url = f"{self.base_url}/me/drive/items/{item_id}"
|
|
103
|
+
response = self._get(url)
|
|
104
|
+
return self._handle_response(response)
|
|
95
105
|
|
|
96
|
-
def
|
|
97
|
-
self, folder_name: str, folder_path: str | None = None
|
|
98
|
-
) -> dict[str, Any]:
|
|
106
|
+
def create_folder(self, name: str, parent_id: str = "root") -> dict[str, Any]:
|
|
99
107
|
"""
|
|
100
|
-
Creates a new folder with a
|
|
101
|
-
|
|
108
|
+
Creates a new folder with a specified name within a parent directory, which defaults to the root. Returns metadata for the new folder. Unlike `create_folder_and_list`, this function only creates the folder and returns its specific metadata, not the parent directory's contents.
|
|
109
|
+
|
|
102
110
|
Args:
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
111
|
+
name (str): The name of the new folder.
|
|
112
|
+
parent_id (str, optional): The ID of the parent folder. Defaults to 'root'.
|
|
113
|
+
|
|
106
114
|
Returns:
|
|
107
|
-
|
|
108
|
-
|
|
115
|
+
A dictionary containing the new folder's metadata.
|
|
116
|
+
|
|
109
117
|
Tags:
|
|
110
|
-
important
|
|
118
|
+
create, folder, directory, important
|
|
111
119
|
"""
|
|
112
|
-
if
|
|
113
|
-
|
|
114
|
-
else:
|
|
115
|
-
folder = self.client.me.drive.root
|
|
116
|
-
folder.create_folder(folder_name).execute_query()
|
|
117
|
-
return self.list_folders(folder_path)
|
|
120
|
+
if not name:
|
|
121
|
+
raise ValueError("Folder name cannot be empty.")
|
|
118
122
|
|
|
119
|
-
|
|
123
|
+
url = f"{self.base_url}/me/drive/items/{parent_id}/children"
|
|
124
|
+
data = {"name": name, "folder": {}, "@microsoft.graph.conflictBehavior": "rename"}
|
|
125
|
+
response = self._post(url, data=data)
|
|
126
|
+
return self._handle_response(response)
|
|
127
|
+
|
|
128
|
+
def delete_item(self, item_id: str) -> dict[str, Any]:
|
|
120
129
|
"""
|
|
121
|
-
|
|
122
|
-
|
|
130
|
+
Permanently deletes a specified file or folder from SharePoint using its unique item ID. This versatile function can remove any type of drive item, distinguished from functions that only list or create specific types. A successful deletion returns an empty response, confirming the item's removal.
|
|
131
|
+
|
|
123
132
|
Args:
|
|
124
|
-
|
|
125
|
-
|
|
133
|
+
item_id (str): The ID of the item to delete.
|
|
134
|
+
|
|
126
135
|
Returns:
|
|
127
|
-
|
|
128
|
-
|
|
136
|
+
An empty dictionary if successful.
|
|
137
|
+
|
|
129
138
|
Tags:
|
|
130
|
-
important
|
|
139
|
+
delete, remove, file, folder, important
|
|
131
140
|
"""
|
|
132
|
-
|
|
133
|
-
|
|
141
|
+
if not item_id:
|
|
142
|
+
raise ValueError("Missing required parameter 'item_id'.")
|
|
143
|
+
|
|
144
|
+
url = f"{self.base_url}/me/drive/items/{item_id}"
|
|
145
|
+
response = self._delete(url)
|
|
146
|
+
return self._handle_response(response)
|
|
147
|
+
|
|
148
|
+
def download_file(self, item_id: str) -> dict[str, Any]:
|
|
149
|
+
"""
|
|
150
|
+
Retrieves a temporary, pre-authenticated download URL for a specific file using its item ID. This function provides a link for subsequent download, differing from `get_document_content` which directly fetches the file's raw content. The URL is returned within a dictionary.
|
|
134
151
|
|
|
135
|
-
return [
|
|
136
|
-
{
|
|
137
|
-
"name": f.name,
|
|
138
|
-
"url": f.properties.get("ServerRelativeUrl"),
|
|
139
|
-
"size": f.properties.get("Length"),
|
|
140
|
-
"created": _to_iso_optional(f.properties.get("TimeCreated")),
|
|
141
|
-
"modified": _to_iso_optional(f.properties.get("TimeLastModified")),
|
|
142
|
-
}
|
|
143
|
-
for f in files
|
|
144
|
-
]
|
|
145
|
-
|
|
146
|
-
def upload_text_file(
|
|
147
|
-
self, file_path: str, file_name: str, content: str
|
|
148
|
-
) -> dict[str, Any]:
|
|
149
|
-
"""
|
|
150
|
-
Uploads string content to a new file within a specified SharePoint folder path. After creation, it returns an updated list of all documents and their metadata residing in that folder, effectively confirming the file was added successfully.
|
|
151
|
-
|
|
152
152
|
Args:
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
content (str): The content to write into the document.
|
|
156
|
-
|
|
153
|
+
item_id (str): The ID of the file to download.
|
|
154
|
+
|
|
157
155
|
Returns:
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
Tags:
|
|
156
|
+
A dictionary containing the download URL for the file under the key '@microsoft.graph.downloadUrl'.
|
|
157
|
+
|
|
158
|
+
Tags:
|
|
159
|
+
download, file, get, important
|
|
161
160
|
"""
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
161
|
+
if not item_id:
|
|
162
|
+
raise ValueError("Missing required parameter 'item_id'.")
|
|
163
|
+
|
|
164
|
+
url = f"{self.base_url}/me/drive/items/{item_id}"
|
|
165
|
+
response = self._get(url)
|
|
166
|
+
metadata = self._handle_response(response)
|
|
167
|
+
download_url = metadata.get("@microsoft.graph.downloadUrl")
|
|
168
|
+
if not download_url:
|
|
169
|
+
raise ValueError("Could not retrieve download URL for the item.")
|
|
170
|
+
return {"download_url": download_url}
|
|
167
171
|
|
|
168
|
-
def
|
|
172
|
+
def upload_file(self, file_path: str, parent_id: str = "root", file_name: str | None = None) -> dict[str, Any]:
|
|
169
173
|
"""
|
|
170
|
-
|
|
171
|
-
|
|
174
|
+
Uploads a local binary file (under 4MB) from a given path to a specified SharePoint folder. Unlike `upload_text_file`, which uploads string content, this function reads from the filesystem. The destination filename can be customized, and it returns the new file's metadata upon completion.
|
|
175
|
+
|
|
172
176
|
Args:
|
|
173
|
-
file_path (str): The path to the
|
|
174
|
-
|
|
177
|
+
file_path (str): The local path to the file to upload.
|
|
178
|
+
parent_id (str, optional): The ID of the folder to upload the file to. Defaults to 'root'.
|
|
179
|
+
file_name (str, optional): The name to give the uploaded file. If not provided, the local filename is used.
|
|
180
|
+
|
|
175
181
|
Returns:
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
Tags: important
|
|
179
|
-
"""
|
|
180
|
-
file = self.client.me.drive.root.get_by_path(file_path).get().execute_query()
|
|
181
|
-
content_stream = BytesIO()
|
|
182
|
-
file.download(content_stream).execute_query()
|
|
183
|
-
content_stream.seek(0)
|
|
184
|
-
content = content_stream.read()
|
|
185
|
-
|
|
186
|
-
is_text_file = file_path.lower().endswith(
|
|
187
|
-
(".txt", ".csv", ".json", ".xml", ".html", ".md", ".js", ".css", ".py")
|
|
188
|
-
)
|
|
189
|
-
content_dict = (
|
|
190
|
-
{"content": content.decode("utf-8")}
|
|
191
|
-
if is_text_file
|
|
192
|
-
else {"content_base64": base64.b64encode(content).decode("ascii")}
|
|
193
|
-
)
|
|
194
|
-
return {
|
|
195
|
-
"name": file_path.split("/")[-1],
|
|
196
|
-
"content_type": "text" if is_text_file else "binary",
|
|
197
|
-
**content_dict,
|
|
198
|
-
"size": len(content),
|
|
199
|
-
}
|
|
182
|
+
A dictionary containing the uploaded file's metadata.
|
|
200
183
|
|
|
201
|
-
|
|
184
|
+
Tags:
|
|
185
|
+
upload, file, put, important
|
|
202
186
|
"""
|
|
203
|
-
|
|
204
|
-
|
|
187
|
+
if not os.path.exists(file_path):
|
|
188
|
+
raise FileNotFoundError(f"The file was not found at path: {file_path}")
|
|
189
|
+
|
|
190
|
+
if not file_name:
|
|
191
|
+
file_name = os.path.basename(file_path)
|
|
192
|
+
|
|
193
|
+
url = f"{self.base_url}/me/drive/items/{parent_id}:/{file_name}:/content"
|
|
194
|
+
with open(file_path, "rb") as f:
|
|
195
|
+
data = f.read()
|
|
196
|
+
response = self._put(url, data=data, content_type="application/octet-stream")
|
|
197
|
+
return self._handle_response(response)
|
|
198
|
+
|
|
199
|
+
def list_folders(self, item_id: str = "root") -> dict[str, Any]:
|
|
200
|
+
"""
|
|
201
|
+
Retrieves a list of only the folders within a specified parent directory in SharePoint. Unlike `_list_drive_items` which returns all items, this function filters the results to exclude files. Defaults to the root directory if no parent `item_id` is provided.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
item_id (str, optional): The ID of the folder to list from. Defaults to 'root'.
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
A dictionary containing the list of folders.
|
|
208
|
+
|
|
209
|
+
Tags:
|
|
210
|
+
list, folders, directories, important
|
|
211
|
+
"""
|
|
212
|
+
all_items = self._list_drive_items(item_id=item_id)
|
|
213
|
+
folders = [item for item in all_items.get("value", []) if "folder" in item]
|
|
214
|
+
return {"value": folders}
|
|
215
|
+
|
|
216
|
+
def list_files(self, item_id: str = "root") -> dict[str, Any]:
|
|
217
|
+
"""
|
|
218
|
+
Retrieves a list of files within a specified SharePoint folder, defaulting to the root. Unlike `_list_drive_items` which fetches all items, this function filters the results to exclusively return items identified as files, excluding any subdirectories.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
item_id (str, optional): The ID of the folder to list files from. Defaults to 'root'.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
A dictionary containing the list of files.
|
|
225
|
+
|
|
226
|
+
Tags:
|
|
227
|
+
list, files, documents, important
|
|
228
|
+
"""
|
|
229
|
+
all_items = self._list_drive_items(item_id=item_id)
|
|
230
|
+
files = [item for item in all_items.get("value", []) if "file" in item]
|
|
231
|
+
return {"value": files}
|
|
232
|
+
|
|
233
|
+
def create_folder_and_list(self, name: str, parent_id: str = "root") -> dict[str, Any]:
|
|
234
|
+
"""
|
|
235
|
+
Performs a composite action: creates a new folder, then lists all items (files and folders) within that parent directory. This confirms creation by returning the parent's updated contents, distinct from `create_folder` which only returns the new folder's metadata.
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
name (str): The name of the new folder.
|
|
239
|
+
parent_id (str, optional): The ID of the parent folder. Defaults to 'root'.
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
A dictionary containing the list of items in the parent folder after creation.
|
|
243
|
+
|
|
244
|
+
Tags:
|
|
245
|
+
create, folder, list, important
|
|
246
|
+
"""
|
|
247
|
+
self.create_folder(name=name, parent_id=parent_id)
|
|
248
|
+
return self._list_drive_items(item_id=parent_id)
|
|
249
|
+
|
|
250
|
+
def upload_text_file(self, content: str, parent_id: str = "root", file_name: str = "new_file.txt") -> dict[str, Any]:
|
|
251
|
+
"""
|
|
252
|
+
Creates and uploads a new file to SharePoint directly from a string of text content. Unlike `upload_file`, which requires a local file path, this function is specifically for creating a text file from in-memory string data, with a customizable name and destination folder.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
content (str): The text content to upload.
|
|
256
|
+
parent_id (str, optional): The ID of the folder to upload the file to. Defaults to 'root'.
|
|
257
|
+
file_name (str, optional): The name to give the uploaded file. Defaults to 'new_file.txt'.
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
A dictionary containing the uploaded file's metadata.
|
|
261
|
+
|
|
262
|
+
Tags:
|
|
263
|
+
upload, text, file, create, important
|
|
264
|
+
"""
|
|
265
|
+
if not file_name:
|
|
266
|
+
raise ValueError("File name cannot be empty.")
|
|
267
|
+
|
|
268
|
+
url = f"{self.base_url}/me/drive/items/{parent_id}:/{file_name}:/content"
|
|
269
|
+
data = content.encode("utf-8")
|
|
270
|
+
response = self._put(url, data=data, content_type="text/plain")
|
|
271
|
+
return self._handle_response(response)
|
|
272
|
+
|
|
273
|
+
def get_document_content(self, item_id: str) -> dict[str, Any]:
|
|
274
|
+
"""
|
|
275
|
+
Retrieves the content of a specific file by its item ID and returns it directly as base64-encoded data. This function is distinct from `download_file`, which only provides a temporary URL for the content, and from `get_item_metadata`, which returns file attributes without the content itself. The function fetches the content by following the file's pre-authenticated download URL.
|
|
276
|
+
|
|
205
277
|
Args:
|
|
206
|
-
|
|
207
|
-
|
|
278
|
+
item_id (str): The ID of the file.
|
|
279
|
+
|
|
208
280
|
Returns:
|
|
209
|
-
|
|
210
|
-
|
|
281
|
+
dict[str, Any]: A dictionary containing the file details:
|
|
282
|
+
- 'type' (str): The general type of the file (e.g., "image", "audio", "video", "file").
|
|
283
|
+
- 'data' (str): The base64 encoded content of the file.
|
|
284
|
+
- 'mime_type' (str): The MIME type of the file.
|
|
285
|
+
- 'file_name' (str): The name of the file.
|
|
286
|
+
|
|
211
287
|
Tags:
|
|
212
|
-
important
|
|
288
|
+
get, content, read, file, important
|
|
213
289
|
"""
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
290
|
+
if not item_id:
|
|
291
|
+
raise ValueError("Missing required parameter 'item_id'.")
|
|
292
|
+
|
|
293
|
+
metadata = self.get_item_metadata(item_id=item_id)
|
|
294
|
+
file_metadata = metadata.get("file")
|
|
295
|
+
if not file_metadata:
|
|
296
|
+
raise ValueError(f"Item with ID '{item_id}' is not a file.")
|
|
297
|
+
|
|
298
|
+
file_mime_type = file_metadata.get("mimeType", "application/octet-stream")
|
|
299
|
+
file_name = metadata.get("name")
|
|
300
|
+
|
|
301
|
+
download_url = metadata.get("@microsoft.graph.downloadUrl")
|
|
302
|
+
if not download_url:
|
|
303
|
+
logger.error(f"Could not find @microsoft.graph.downloadUrl in metadata for item {item_id}")
|
|
304
|
+
raise ValueError("Could not retrieve download URL for the item.")
|
|
305
|
+
|
|
306
|
+
response = self._get(download_url)
|
|
307
|
+
|
|
308
|
+
response.raise_for_status()
|
|
309
|
+
|
|
310
|
+
content = response.content
|
|
311
|
+
|
|
312
|
+
attachment_type = file_mime_type.split("/")[0] if "/" in file_mime_type else "file"
|
|
313
|
+
if attachment_type not in ["image", "audio", "video", "text"]:
|
|
314
|
+
attachment_type = "file"
|
|
315
|
+
|
|
316
|
+
return {
|
|
317
|
+
"type": attachment_type,
|
|
318
|
+
"data": content,
|
|
319
|
+
"mime_type": file_mime_type,
|
|
320
|
+
"file_name": file_name,
|
|
321
|
+
}
|
|
217
322
|
|
|
218
323
|
def list_tools(self):
|
|
219
324
|
return [
|
|
325
|
+
self.get_drive_info,
|
|
326
|
+
self.search_files,
|
|
327
|
+
self.get_item_metadata,
|
|
328
|
+
self.create_folder,
|
|
329
|
+
self.delete_item,
|
|
330
|
+
self.download_file,
|
|
331
|
+
self.upload_file,
|
|
332
|
+
self.get_my_profile,
|
|
220
333
|
self.list_folders,
|
|
221
|
-
self.create_folder_and_list,
|
|
222
334
|
self.list_files,
|
|
335
|
+
self.create_folder_and_list,
|
|
223
336
|
self.upload_text_file,
|
|
224
337
|
self.get_document_content,
|
|
225
|
-
|
|
226
|
-
]
|
|
338
|
+
]
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ShortcutApp MCP Server
|
|
2
2
|
|
|
3
|
-
An MCP Server for the
|
|
3
|
+
An MCP Server for the ShortcutApp API.
|
|
4
4
|
|
|
5
5
|
## 🛠️ Tool List
|
|
6
6
|
|
|
7
|
-
This is automatically generated from OpenAPI schema for the
|
|
7
|
+
This is automatically generated from OpenAPI schema for the ShortcutApp API.
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
| Tool | Description |
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# SlackApp MCP Server
|
|
2
|
+
|
|
3
|
+
An MCP Server for the SlackApp API.
|
|
4
|
+
|
|
5
|
+
## 🛠️ Tool List
|
|
6
|
+
|
|
7
|
+
This is automatically generated from OpenAPI schema for the SlackApp API.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
| Tool | Description |
|
|
11
|
+
|------|-------------|
|
|
12
|
+
| `chat_delete` | Deletes a specific message from a Slack channel. It identifies the message using its channel ID and timestamp (`ts`). This function is distinct from `chat_update` which modifies a message, and `chat_post_message` which sends a new one. |
|
|
13
|
+
| `chat_post_message` | Posts a new message to a specified Slack channel. It supports rich content formats like text, attachments, and blocks, along with options for threading and authorship. This function creates new messages, unlike `chat_update` which modifies existing ones or `chat_delete` which removes them. |
|
|
14
|
+
| `chat_update` | Updates a specific, existing message in a Slack channel, identified by its timestamp. It modifies content such as text, blocks, or attachments by calling the `chat.update` API endpoint, distinguishing it from functions that create (`chat_post_message`) or delete (`chat_delete`) messages. |
|
|
15
|
+
| `conversations_history` | Fetches the message history for a specific Slack conversation or channel. Supports filtering messages by timestamp and pagination using a cursor. This method retrieves messages *within* a conversation, unlike `conversations_list` which retrieves a list of available conversations. |
|
|
16
|
+
| `conversations_list` | Fetches a paginated list of channel-like conversations in a workspace. Allows filtering by type and excluding archived channels. This function lists available conversations, unlike `conversations_history` which retrieves the message history from within a specific conversation. |
|
|
17
|
+
| `reactions_add` | Adds a specific emoji reaction to a message in a Slack channel, identifying the message by its channel ID and timestamp. This method creates a new reaction, unlike `reactions_get` or `reactions_list` which retrieve existing reaction data for items or users. |
|
|
18
|
+
| `get_reactions_for_item` | Retrieves all reactions for a single item, such as a message, file, or file comment. This function targets reactions *on* a specific item, unlike `reactions_list` which gets reactions created *by* a user. |
|
|
19
|
+
| `get_user_reactions` | Retrieves a paginated list of items (e.g., messages, files) that a specific user has reacted to. Unlike `reactions_get`, which fetches all reactions for a single item, this function lists all items a given user has reacted to across Slack. |
|
|
20
|
+
| `search_messages` | Searches a Slack workspace for messages matching a specific query. It supports advanced control over results through pagination, sorting by relevance or timestamp, and an option to highlight search terms within the returned message content. |
|
|
21
|
+
| `team_info` | Fetches details for a Slack team, such as name and domain, by calling the `team.info` API endpoint. This function requires an authentication token and can optionally target a specific team by its ID, distinguishing it from user or channel-specific functions. |
|
|
22
|
+
| `get_user_info` | Fetches detailed profile information for a single Slack user, identified by their user ID. Unlike `users_list`, which retrieves all workspace members, this function targets an individual and can optionally include their locale information. It directly calls the `users.info` Slack API endpoint. |
|
|
23
|
+
| `users_list` | Fetches a paginated list of all users in a Slack workspace, including deactivated members. Unlike `users_info` which retrieves a single user's details, this function returns a collection and supports limiting results or including locale data through optional parameters. |
|