universal-mcp-applications 0.1.22__py3-none-any.whl → 0.1.39rc8__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 +92 -238
- universal_mcp/applications/airtable/app.py +23 -122
- universal_mcp/applications/apollo/app.py +122 -475
- universal_mcp/applications/asana/app.py +605 -1755
- universal_mcp/applications/aws_s3/app.py +36 -103
- universal_mcp/applications/bill/app.py +644 -2055
- universal_mcp/applications/box/app.py +1246 -4159
- universal_mcp/applications/braze/app.py +410 -1476
- universal_mcp/applications/browser_use/README.md +15 -1
- universal_mcp/applications/browser_use/__init__.py +1 -0
- universal_mcp/applications/browser_use/app.py +94 -37
- universal_mcp/applications/cal_com_v2/app.py +207 -625
- universal_mcp/applications/calendly/app.py +103 -242
- universal_mcp/applications/canva/app.py +75 -140
- universal_mcp/applications/clickup/app.py +331 -798
- universal_mcp/applications/coda/app.py +240 -520
- universal_mcp/applications/confluence/app.py +497 -1285
- universal_mcp/applications/contentful/app.py +36 -151
- universal_mcp/applications/crustdata/app.py +42 -121
- universal_mcp/applications/dialpad/app.py +451 -924
- universal_mcp/applications/digitalocean/app.py +2071 -6082
- 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/README.md +8 -4
- universal_mcp/applications/exa/app.py +408 -186
- universal_mcp/applications/falai/app.py +24 -101
- universal_mcp/applications/figma/app.py +91 -175
- universal_mcp/applications/file_system/app.py +2 -13
- universal_mcp/applications/firecrawl/app.py +186 -163
- universal_mcp/applications/fireflies/app.py +59 -281
- universal_mcp/applications/fpl/app.py +92 -529
- 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 +66 -175
- universal_mcp/applications/github/app.py +28 -65
- universal_mcp/applications/gong/app.py +140 -300
- universal_mcp/applications/google_calendar/app.py +26 -78
- universal_mcp/applications/google_docs/app.py +324 -354
- universal_mcp/applications/google_drive/app.py +194 -793
- universal_mcp/applications/google_gemini/app.py +29 -64
- universal_mcp/applications/google_mail/README.md +1 -0
- universal_mcp/applications/google_mail/app.py +93 -214
- universal_mcp/applications/google_searchconsole/app.py +25 -58
- universal_mcp/applications/google_sheet/app.py +174 -623
- universal_mcp/applications/google_sheet/helper.py +26 -53
- universal_mcp/applications/hashnode/app.py +57 -269
- universal_mcp/applications/heygen/app.py +77 -155
- universal_mcp/applications/http_tools/app.py +10 -32
- universal_mcp/applications/hubspot/README.md +1 -1
- universal_mcp/applications/hubspot/app.py +7508 -99
- universal_mcp/applications/jira/app.py +2419 -8334
- universal_mcp/applications/klaviyo/app.py +737 -1619
- universal_mcp/applications/linkedin/README.md +23 -4
- universal_mcp/applications/linkedin/app.py +861 -155
- universal_mcp/applications/mailchimp/app.py +696 -1851
- universal_mcp/applications/markitdown/app.py +8 -20
- universal_mcp/applications/miro/app.py +333 -815
- universal_mcp/applications/ms_teams/app.py +85 -207
- universal_mcp/applications/neon/app.py +144 -250
- universal_mcp/applications/notion/app.py +36 -51
- universal_mcp/applications/onedrive/README.md +24 -0
- universal_mcp/applications/onedrive/__init__.py +1 -0
- universal_mcp/applications/onedrive/app.py +316 -0
- universal_mcp/applications/openai/app.py +42 -165
- universal_mcp/applications/outlook/README.md +22 -9
- universal_mcp/applications/outlook/app.py +606 -262
- universal_mcp/applications/perplexity/README.md +2 -1
- universal_mcp/applications/perplexity/app.py +162 -20
- universal_mcp/applications/pipedrive/app.py +1021 -3331
- universal_mcp/applications/posthog/app.py +272 -541
- universal_mcp/applications/reddit/app.py +88 -204
- universal_mcp/applications/resend/app.py +41 -107
- universal_mcp/applications/retell/app.py +23 -50
- universal_mcp/applications/rocketlane/app.py +250 -963
- universal_mcp/applications/scraper/README.md +7 -4
- universal_mcp/applications/scraper/app.py +245 -283
- universal_mcp/applications/semanticscholar/app.py +36 -78
- universal_mcp/applications/semrush/app.py +43 -77
- universal_mcp/applications/sendgrid/app.py +826 -1576
- universal_mcp/applications/sentry/app.py +444 -1079
- universal_mcp/applications/serpapi/app.py +40 -143
- universal_mcp/applications/sharepoint/README.md +16 -14
- universal_mcp/applications/sharepoint/app.py +245 -154
- universal_mcp/applications/shopify/app.py +1743 -4479
- universal_mcp/applications/shortcut/app.py +272 -534
- universal_mcp/applications/slack/app.py +58 -109
- universal_mcp/applications/spotify/app.py +206 -405
- universal_mcp/applications/supabase/app.py +174 -283
- universal_mcp/applications/tavily/app.py +2 -2
- universal_mcp/applications/trello/app.py +853 -2816
- 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 +86 -299
- universal_mcp/applications/wrike/app.py +80 -153
- universal_mcp/applications/yahoo_finance/app.py +19 -65
- universal_mcp/applications/youtube/app.py +120 -306
- universal_mcp/applications/zenquotes/app.py +4 -4
- {universal_mcp_applications-0.1.22.dist-info → universal_mcp_applications-0.1.39rc8.dist-info}/METADATA +4 -2
- {universal_mcp_applications-0.1.22.dist-info → universal_mcp_applications-0.1.39rc8.dist-info}/RECORD +113 -117
- {universal_mcp_applications-0.1.22.dist-info → universal_mcp_applications-0.1.39rc8.dist-info}/WHEEL +1 -1
- universal_mcp/applications/hubspot/api_segments/__init__.py +0 -0
- universal_mcp/applications/hubspot/api_segments/api_segment_base.py +0 -54
- universal_mcp/applications/hubspot/api_segments/crm_api.py +0 -7337
- universal_mcp/applications/hubspot/api_segments/marketing_api.py +0 -1467
- universal_mcp/applications/unipile/README.md +0 -28
- universal_mcp/applications/unipile/__init__.py +0 -1
- universal_mcp/applications/unipile/app.py +0 -1077
- {universal_mcp_applications-0.1.22.dist-info → universal_mcp_applications-0.1.39rc8.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,225 +1,316 @@
|
|
|
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
|
-
|
|
7
4
|
from loguru import logger
|
|
8
|
-
from
|
|
9
|
-
from universal_mcp.applications.application import BaseApplication
|
|
5
|
+
from universal_mcp.applications.application import APIApplication
|
|
10
6
|
from universal_mcp.integrations import Integration
|
|
11
7
|
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
"""Converts a datetime object to ISO format string, or returns None if the object is None."""
|
|
15
|
-
if dt_obj is not None:
|
|
16
|
-
return dt_obj.isoformat()
|
|
17
|
-
return None
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class SharepointApp(BaseApplication):
|
|
9
|
+
class SharepointApp(APIApplication):
|
|
21
10
|
"""
|
|
22
|
-
|
|
11
|
+
Application for interacting with Microsoft SharePoint API (via Microsoft Graph).
|
|
12
|
+
Provides tools to manage files, folders, and access Drive information.
|
|
23
13
|
"""
|
|
24
14
|
|
|
25
|
-
def __init__(self, integration: Integration
|
|
26
|
-
""
|
|
15
|
+
def __init__(self, integration: Integration | None = None, **kwargs) -> None:
|
|
16
|
+
super().__init__(name="sharepoint", integration=integration, **kwargs)
|
|
17
|
+
self.base_url = "https://graph.microsoft.com/v1.0"
|
|
18
|
+
|
|
19
|
+
async def get_my_profile(self) -> dict[str, Any]:
|
|
20
|
+
"""
|
|
21
|
+
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.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
dict[str, Any]: A dictionary containing the user's id and userPrincipalName.
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
HTTPStatusError: If the API request fails.
|
|
28
|
+
|
|
29
|
+
Tags:
|
|
30
|
+
profile, user, account
|
|
31
|
+
"""
|
|
32
|
+
url = f"{self.base_url}/me"
|
|
33
|
+
query_params = {"$select": "id,userPrincipalName"}
|
|
34
|
+
response = await self._aget(url, params=query_params)
|
|
35
|
+
return self._handle_response(response)
|
|
36
|
+
|
|
37
|
+
async def get_drive_info(self) -> dict[str, Any]:
|
|
38
|
+
"""
|
|
39
|
+
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.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
A dictionary containing drive information.
|
|
43
|
+
|
|
44
|
+
Tags:
|
|
45
|
+
drive, storage, quota, info
|
|
46
|
+
"""
|
|
47
|
+
url = f"{self.base_url}/me/drive"
|
|
48
|
+
response = await self._aget(url)
|
|
49
|
+
return self._handle_response(response)
|
|
50
|
+
|
|
51
|
+
def _list_drive_items(self, item_id: str = "root") -> dict[str, Any]:
|
|
52
|
+
"""
|
|
53
|
+
Lists the files and folders in the current user's SharePoint.
|
|
54
|
+
|
|
27
55
|
Args:
|
|
28
|
-
|
|
56
|
+
item_id (str, optional): The ID of the folder to list. Defaults to 'root'.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
A dictionary containing the list of files and folders.
|
|
29
60
|
"""
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
self.
|
|
33
|
-
|
|
61
|
+
url = f"{self.base_url}/me/drive/items/{item_id}/children"
|
|
62
|
+
response = self._get(url)
|
|
63
|
+
return self._handle_response(response)
|
|
64
|
+
|
|
65
|
+
async def search_files(self, query: str) -> dict[str, Any]:
|
|
66
|
+
"""
|
|
67
|
+
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.
|
|
34
68
|
|
|
35
|
-
|
|
36
|
-
|
|
69
|
+
Args:
|
|
70
|
+
query (str): The search query.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
A dictionary containing the search results.
|
|
74
|
+
|
|
75
|
+
Tags:
|
|
76
|
+
search, find, query, files, important
|
|
37
77
|
"""
|
|
38
|
-
|
|
78
|
+
if not query:
|
|
79
|
+
raise ValueError("Search query cannot be empty.")
|
|
80
|
+
url = f"{self.base_url}/me/drive/root/search(q='{query}')"
|
|
81
|
+
response = await self._aget(url)
|
|
82
|
+
return self._handle_response(response)
|
|
83
|
+
|
|
84
|
+
async def get_item_metadata(self, item_id: str) -> dict[str, Any]:
|
|
85
|
+
"""
|
|
86
|
+
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.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
item_id (str): The ID of the file or folder.
|
|
39
90
|
|
|
40
91
|
Returns:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if not
|
|
47
|
-
raise ValueError("
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"""
|
|
56
|
-
access_token = credentials.get("access_token")
|
|
57
|
-
refresh_token = credentials.get("refresh_token")
|
|
58
|
-
return {
|
|
59
|
-
"access_token": access_token,
|
|
60
|
-
"refresh_token": refresh_token,
|
|
61
|
-
"token_type": "Bearer",
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if self._client is None:
|
|
65
|
-
self._client = GraphClient(token_callback=_acquire_token)
|
|
66
|
-
# Get me
|
|
67
|
-
me = self._client.me.get().execute_query()
|
|
68
|
-
logger.debug(me.properties)
|
|
69
|
-
# Get sites
|
|
70
|
-
sites = self._client.sites.root.get().execute_query()
|
|
71
|
-
self._site_url = sites.properties.get("id")
|
|
72
|
-
return self._client
|
|
73
|
-
|
|
74
|
-
def list_folders(self, folder_path: str | None = None) -> list[dict[str, Any]]:
|
|
75
|
-
"""
|
|
76
|
-
Retrieves a list of immediate subfolder names within a specified SharePoint directory. If no path is provided, it defaults to the root drive. This function is distinct from `list_files`, as it exclusively lists directories, not files.
|
|
92
|
+
A dictionary containing the item's metadata.
|
|
93
|
+
|
|
94
|
+
Tags:
|
|
95
|
+
metadata, info, file, folder
|
|
96
|
+
"""
|
|
97
|
+
if not item_id:
|
|
98
|
+
raise ValueError("Missing required parameter 'item_id'.")
|
|
99
|
+
url = f"{self.base_url}/me/drive/items/{item_id}"
|
|
100
|
+
response = await self._aget(url)
|
|
101
|
+
return self._handle_response(response)
|
|
102
|
+
|
|
103
|
+
async def create_folder(self, name: str, parent_id: str = "root") -> dict[str, Any]:
|
|
104
|
+
"""
|
|
105
|
+
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.
|
|
77
106
|
|
|
78
107
|
Args:
|
|
79
|
-
|
|
108
|
+
name (str): The name of the new folder.
|
|
109
|
+
parent_id (str, optional): The ID of the parent folder. Defaults to 'root'.
|
|
80
110
|
|
|
81
111
|
Returns:
|
|
82
|
-
|
|
112
|
+
A dictionary containing the new folder's metadata.
|
|
83
113
|
|
|
84
114
|
Tags:
|
|
85
|
-
important
|
|
115
|
+
create, folder, directory, important
|
|
86
116
|
"""
|
|
87
|
-
if
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
117
|
+
if not name:
|
|
118
|
+
raise ValueError("Folder name cannot be empty.")
|
|
119
|
+
url = f"{self.base_url}/me/drive/items/{parent_id}/children"
|
|
120
|
+
data = {"name": name, "folder": {}, "@microsoft.graph.conflictBehavior": "rename"}
|
|
121
|
+
response = await self._apost(url, data=data)
|
|
122
|
+
return self._handle_response(response)
|
|
123
|
+
|
|
124
|
+
async def delete_item(self, item_id: str) -> dict[str, Any]:
|
|
125
|
+
"""
|
|
126
|
+
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.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
item_id (str): The ID of the item to delete.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
An empty dictionary if successful.
|
|
92
133
|
|
|
93
|
-
|
|
134
|
+
Tags:
|
|
135
|
+
delete, remove, file, folder, important
|
|
136
|
+
"""
|
|
137
|
+
if not item_id:
|
|
138
|
+
raise ValueError("Missing required parameter 'item_id'.")
|
|
139
|
+
url = f"{self.base_url}/me/drive/items/{item_id}"
|
|
140
|
+
response = await self._adelete(url)
|
|
141
|
+
return self._handle_response(response)
|
|
94
142
|
|
|
95
|
-
def
|
|
96
|
-
self, folder_name: str, folder_path: str | None = None
|
|
97
|
-
) -> dict[str, Any]:
|
|
143
|
+
async def download_file(self, item_id: str) -> dict[str, Any]:
|
|
98
144
|
"""
|
|
99
|
-
|
|
145
|
+
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.
|
|
100
146
|
|
|
101
147
|
Args:
|
|
102
|
-
|
|
103
|
-
folder_path (str | None, optional): The path to the parent folder. If None, creates in the root.
|
|
148
|
+
item_id (str): The ID of the file to download.
|
|
104
149
|
|
|
105
150
|
Returns:
|
|
106
|
-
|
|
151
|
+
A dictionary containing the download URL for the file under the key '@microsoft.graph.downloadUrl'.
|
|
107
152
|
|
|
108
153
|
Tags:
|
|
109
|
-
important
|
|
154
|
+
download, file, get, important
|
|
110
155
|
"""
|
|
111
|
-
if
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
156
|
+
if not item_id:
|
|
157
|
+
raise ValueError("Missing required parameter 'item_id'.")
|
|
158
|
+
url = f"{self.base_url}/me/drive/items/{item_id}"
|
|
159
|
+
response = await self._aget(url)
|
|
160
|
+
metadata = self._handle_response(response)
|
|
161
|
+
download_url = metadata.get("@microsoft.graph.downloadUrl")
|
|
162
|
+
if not download_url:
|
|
163
|
+
raise ValueError("Could not retrieve download URL for the item.")
|
|
164
|
+
return {"download_url": download_url}
|
|
165
|
+
|
|
166
|
+
async def upload_file(self, file_path: str, parent_id: str = "root", file_name: str | None = None) -> dict[str, Any]:
|
|
167
|
+
"""
|
|
168
|
+
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.
|
|
117
169
|
|
|
118
|
-
|
|
170
|
+
Args:
|
|
171
|
+
file_path (str): The local path to the file to upload.
|
|
172
|
+
parent_id (str, optional): The ID of the folder to upload the file to. Defaults to 'root'.
|
|
173
|
+
file_name (str, optional): The name to give the uploaded file. If not provided, the local filename is used.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
A dictionary containing the uploaded file's metadata.
|
|
177
|
+
|
|
178
|
+
Tags:
|
|
179
|
+
upload, file, put, important
|
|
119
180
|
"""
|
|
120
|
-
|
|
181
|
+
if not os.path.exists(file_path):
|
|
182
|
+
raise FileNotFoundError(f"The file was not found at path: {file_path}")
|
|
183
|
+
if not file_name:
|
|
184
|
+
file_name = os.path.basename(file_path)
|
|
185
|
+
url = f"{self.base_url}/me/drive/items/{parent_id}:/{file_name}:/content"
|
|
186
|
+
with open(file_path, "rb") as f:
|
|
187
|
+
data = f.read()
|
|
188
|
+
response = await self._aput(url, data=data, content_type="application/octet-stream")
|
|
189
|
+
return self._handle_response(response)
|
|
190
|
+
|
|
191
|
+
async def list_folders(self, item_id: str = "root") -> dict[str, Any]:
|
|
192
|
+
"""
|
|
193
|
+
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.
|
|
121
194
|
|
|
122
195
|
Args:
|
|
123
|
-
|
|
196
|
+
item_id (str, optional): The ID of the folder to list from. Defaults to 'root'.
|
|
124
197
|
|
|
125
198
|
Returns:
|
|
126
|
-
|
|
199
|
+
A dictionary containing the list of folders.
|
|
127
200
|
|
|
128
201
|
Tags:
|
|
129
|
-
important
|
|
202
|
+
list, folders, directories, important
|
|
130
203
|
"""
|
|
131
|
-
|
|
132
|
-
|
|
204
|
+
all_items = self._list_drive_items(item_id=item_id)
|
|
205
|
+
folders = [item for item in all_items.get("value", []) if "folder" in item]
|
|
206
|
+
return {"value": folders}
|
|
133
207
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
208
|
+
async def list_files(self, item_id: str = "root") -> dict[str, Any]:
|
|
209
|
+
"""
|
|
210
|
+
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.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
item_id (str, optional): The ID of the folder to list files from. Defaults to 'root'.
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
A dictionary containing the list of files.
|
|
217
|
+
|
|
218
|
+
Tags:
|
|
219
|
+
list, files, documents, important
|
|
220
|
+
"""
|
|
221
|
+
all_items = self._list_drive_items(item_id=item_id)
|
|
222
|
+
files = [item for item in all_items.get("value", []) if "file" in item]
|
|
223
|
+
return {"value": files}
|
|
144
224
|
|
|
145
|
-
def
|
|
146
|
-
self, file_path: str, file_name: str, content: str
|
|
147
|
-
) -> dict[str, Any]:
|
|
225
|
+
async def create_folder_and_list(self, name: str, parent_id: str = "root") -> dict[str, Any]:
|
|
148
226
|
"""
|
|
149
|
-
|
|
227
|
+
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.
|
|
150
228
|
|
|
151
229
|
Args:
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
content (str): The content to write into the document.
|
|
230
|
+
name (str): The name of the new folder.
|
|
231
|
+
parent_id (str, optional): The ID of the parent folder. Defaults to 'root'.
|
|
155
232
|
|
|
156
233
|
Returns:
|
|
157
|
-
|
|
234
|
+
A dictionary containing the list of items in the parent folder after creation.
|
|
158
235
|
|
|
159
|
-
Tags:
|
|
236
|
+
Tags:
|
|
237
|
+
create, folder, list, important
|
|
160
238
|
"""
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
file_io.name = file_name
|
|
164
|
-
file.upload_file(file_io).execute_query()
|
|
165
|
-
return self.list_documents(file_path)
|
|
239
|
+
await self.create_folder(name=name, parent_id=parent_id)
|
|
240
|
+
return self._list_drive_items(item_id=parent_id)
|
|
166
241
|
|
|
167
|
-
def
|
|
242
|
+
async def upload_text_file(self, content: str, parent_id: str = "root", file_name: str = "new_file.txt") -> dict[str, Any]:
|
|
168
243
|
"""
|
|
169
|
-
|
|
244
|
+
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.
|
|
170
245
|
|
|
171
246
|
Args:
|
|
172
|
-
|
|
247
|
+
content (str): The text content to upload.
|
|
248
|
+
parent_id (str, optional): The ID of the folder to upload the file to. Defaults to 'root'.
|
|
249
|
+
file_name (str, optional): The name to give the uploaded file. Defaults to 'new_file.txt'.
|
|
173
250
|
|
|
174
251
|
Returns:
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
Tags:
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
if is_text_file
|
|
191
|
-
else {"content_base64": base64.b64encode(content).decode("ascii")}
|
|
192
|
-
)
|
|
193
|
-
return {
|
|
194
|
-
"name": file_path.split("/")[-1],
|
|
195
|
-
"content_type": "text" if is_text_file else "binary",
|
|
196
|
-
**content_dict,
|
|
197
|
-
"size": len(content),
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
def delete_document(self, file_path: str):
|
|
201
|
-
"""
|
|
202
|
-
Permanently deletes a specified file from a SharePoint drive using its full path. This is the sole destructive file operation, contrasting with functions that read or create files. It returns `True` on successful deletion and raises an exception on failure, such as if the file is not found.
|
|
252
|
+
A dictionary containing the uploaded file's metadata.
|
|
253
|
+
|
|
254
|
+
Tags:
|
|
255
|
+
upload, text, file, create, important
|
|
256
|
+
"""
|
|
257
|
+
if not file_name:
|
|
258
|
+
raise ValueError("File name cannot be empty.")
|
|
259
|
+
url = f"{self.base_url}/me/drive/items/{parent_id}:/{file_name}:/content"
|
|
260
|
+
data = content.encode("utf-8")
|
|
261
|
+
response = await self._aput(url, data=data, content_type="text/plain")
|
|
262
|
+
return self._handle_response(response)
|
|
263
|
+
|
|
264
|
+
async def get_document_content(self, item_id: str) -> dict[str, Any]:
|
|
265
|
+
"""
|
|
266
|
+
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.
|
|
203
267
|
|
|
204
268
|
Args:
|
|
205
|
-
|
|
269
|
+
item_id (str): The ID of the file.
|
|
206
270
|
|
|
207
271
|
Returns:
|
|
208
|
-
|
|
272
|
+
dict[str, Any]: A dictionary containing the file details:
|
|
273
|
+
- 'type' (str): The general type of the file (e.g., "image", "audio", "video", "file").
|
|
274
|
+
- 'data' (str): The base64 encoded content of the file.
|
|
275
|
+
- 'mime_type' (str): The MIME type of the file.
|
|
276
|
+
- 'file_name' (str): The name of the file.
|
|
209
277
|
|
|
210
278
|
Tags:
|
|
211
|
-
important
|
|
279
|
+
get, content, read, file, important
|
|
212
280
|
"""
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
281
|
+
if not item_id:
|
|
282
|
+
raise ValueError("Missing required parameter 'item_id'.")
|
|
283
|
+
metadata = await self.get_item_metadata(item_id=item_id)
|
|
284
|
+
file_metadata = metadata.get("file")
|
|
285
|
+
if not file_metadata:
|
|
286
|
+
raise ValueError(f"Item with ID '{item_id}' is not a file.")
|
|
287
|
+
file_mime_type = file_metadata.get("mimeType", "application/octet-stream")
|
|
288
|
+
file_name = metadata.get("name")
|
|
289
|
+
download_url = metadata.get("@microsoft.graph.downloadUrl")
|
|
290
|
+
if not download_url:
|
|
291
|
+
logger.error(f"Could not find @microsoft.graph.downloadUrl in metadata for item {item_id}")
|
|
292
|
+
raise ValueError("Could not retrieve download URL for the item.")
|
|
293
|
+
response = await self._aget(download_url)
|
|
294
|
+
response.raise_for_status()
|
|
295
|
+
content = response.content
|
|
296
|
+
attachment_type = file_mime_type.split("/")[0] if "/" in file_mime_type else "file"
|
|
297
|
+
if attachment_type not in ["image", "audio", "video", "text"]:
|
|
298
|
+
attachment_type = "file"
|
|
299
|
+
return {"type": attachment_type, "data": content, "mime_type": file_mime_type, "file_name": file_name}
|
|
216
300
|
|
|
217
301
|
def list_tools(self):
|
|
218
302
|
return [
|
|
303
|
+
self.get_drive_info,
|
|
304
|
+
self.search_files,
|
|
305
|
+
self.get_item_metadata,
|
|
306
|
+
self.create_folder,
|
|
307
|
+
self.delete_item,
|
|
308
|
+
self.download_file,
|
|
309
|
+
self.upload_file,
|
|
310
|
+
self.get_my_profile,
|
|
219
311
|
self.list_folders,
|
|
220
|
-
self.create_folder_and_list,
|
|
221
312
|
self.list_files,
|
|
313
|
+
self.create_folder_and_list,
|
|
222
314
|
self.upload_text_file,
|
|
223
315
|
self.get_document_content,
|
|
224
|
-
self.delete_document,
|
|
225
316
|
]
|