universal-mcp-applications 0.1.25__py3-none-any.whl → 0.1.32__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/google_docs/app.py +6 -2
- universal_mcp/applications/google_gemini/app.py +3 -3
- universal_mcp/applications/google_sheet/app.py +6 -2
- universal_mcp/applications/linkedin/README.md +16 -4
- universal_mcp/applications/linkedin/app.py +748 -153
- 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/outlook/app.py +253 -209
- universal_mcp/applications/reddit/app.py +30 -47
- universal_mcp/applications/scraper/app.py +304 -290
- universal_mcp/applications/sharepoint/README.md +16 -14
- universal_mcp/applications/sharepoint/app.py +267 -154
- universal_mcp/applications/slack/app.py +31 -0
- {universal_mcp_applications-0.1.25.dist-info → universal_mcp_applications-0.1.32.dist-info}/METADATA +2 -2
- {universal_mcp_applications-0.1.25.dist-info → universal_mcp_applications-0.1.32.dist-info}/RECORD +18 -18
- 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.25.dist-info → universal_mcp_applications-0.1.32.dist-info}/WHEEL +0 -0
- {universal_mcp_applications-0.1.25.dist-info → universal_mcp_applications-0.1.32.dist-info}/licenses/LICENSE +0 -0
|
@@ -10,143 +10,176 @@ class OutlookApp(APIApplication):
|
|
|
10
10
|
super().__init__(name="outlook", integration=integration, **kwargs)
|
|
11
11
|
self.base_url = "https://graph.microsoft.com/v1.0"
|
|
12
12
|
|
|
13
|
-
def
|
|
13
|
+
def reply_to_email(
|
|
14
14
|
self,
|
|
15
15
|
message_id: str,
|
|
16
|
+
comment: str,
|
|
16
17
|
user_id: str | None = None,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
) -> Any:
|
|
18
|
+
attachments: list[dict[str, Any]] | None = None,
|
|
19
|
+
) -> dict[str, Any]:
|
|
20
20
|
"""
|
|
21
|
-
Replies to
|
|
21
|
+
Replies to a specific email message.
|
|
22
22
|
|
|
23
23
|
Args:
|
|
24
|
-
message_id (
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
message_id (str): The ID of the email message to reply to.
|
|
25
|
+
comment (str): The body of the reply.
|
|
26
|
+
user_id (str, optional): The ID of the user to send the reply from. Defaults to the authenticated user.
|
|
27
|
+
attachments (list[dict[str, Any]], optional): A list of attachment objects to include in the reply.
|
|
28
|
+
Each attachment dictionary should conform to the Microsoft Graph API specification.
|
|
29
|
+
Example:
|
|
30
|
+
[
|
|
31
|
+
{
|
|
32
|
+
"@odata.type": "#microsoft.graph.fileAttachment",
|
|
33
|
+
"name": "attachment.txt",
|
|
34
|
+
"contentType": "text/plain",
|
|
35
|
+
"contentBytes": "SGVsbG8gV29ybGQh"
|
|
36
|
+
}
|
|
37
|
+
]
|
|
28
38
|
|
|
29
39
|
Returns:
|
|
30
|
-
Any:
|
|
40
|
+
dict[str, Any]: A dictionary confirming the reply action.
|
|
31
41
|
|
|
32
42
|
Raises:
|
|
33
|
-
HTTPStatusError:
|
|
43
|
+
HTTPStatusError: If the API request fails.
|
|
44
|
+
ValueError: If the user_id cannot be retrieved or message_id is missing.
|
|
34
45
|
|
|
35
46
|
Tags:
|
|
36
|
-
|
|
47
|
+
important
|
|
37
48
|
"""
|
|
38
|
-
# If user_id is not provided, get it automatically
|
|
39
49
|
if user_id is None:
|
|
40
|
-
user_info = self.
|
|
50
|
+
user_info = self.get_my_profile()
|
|
41
51
|
user_id = user_info.get("userPrincipalName")
|
|
42
52
|
if not user_id:
|
|
43
|
-
raise ValueError("Could not retrieve user ID from
|
|
44
|
-
if message_id
|
|
45
|
-
raise ValueError("Missing required parameter '
|
|
46
|
-
|
|
47
|
-
request_body_data = {
|
|
48
|
-
|
|
49
|
-
"message":
|
|
50
|
-
|
|
51
|
-
request_body_data = {k: v for k, v in request_body_data.items() if v is not None}
|
|
53
|
+
raise ValueError("Could not retrieve user ID from get_my_profile response.")
|
|
54
|
+
if not message_id:
|
|
55
|
+
raise ValueError("Missing required parameter 'message_id'.")
|
|
56
|
+
|
|
57
|
+
request_body_data = {"comment": comment}
|
|
58
|
+
if attachments:
|
|
59
|
+
request_body_data["message"] = {"attachments": attachments}
|
|
60
|
+
|
|
52
61
|
url = f"{self.base_url}/users/{user_id}/messages/{message_id}/reply"
|
|
53
|
-
|
|
62
|
+
|
|
54
63
|
response = self._post(
|
|
55
64
|
url,
|
|
56
65
|
data=request_body_data,
|
|
57
|
-
params=
|
|
66
|
+
params={},
|
|
58
67
|
content_type="application/json",
|
|
59
68
|
)
|
|
60
69
|
return self._handle_response(response)
|
|
61
70
|
|
|
62
|
-
def
|
|
71
|
+
def send_email(
|
|
63
72
|
self,
|
|
64
|
-
|
|
73
|
+
subject: str,
|
|
74
|
+
body: str,
|
|
75
|
+
to_recipients: list[str],
|
|
65
76
|
user_id: str | None = None,
|
|
66
|
-
|
|
67
|
-
|
|
77
|
+
cc_recipients: list[str] | None = None,
|
|
78
|
+
bcc_recipients: list[str] | None = None,
|
|
79
|
+
attachments: list[dict[str, Any]] | None = None,
|
|
80
|
+
body_content_type: str = "Text",
|
|
81
|
+
save_to_sent_items: bool = True,
|
|
82
|
+
) -> dict[str, Any]:
|
|
68
83
|
"""
|
|
69
|
-
Sends a new email
|
|
84
|
+
Sends a new email.
|
|
70
85
|
|
|
71
86
|
Args:
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
87
|
+
subject (str): The subject of the email.
|
|
88
|
+
body (str): The body of the email.
|
|
89
|
+
to_recipients (list[str]): A list of email addresses for the 'To' recipients.
|
|
90
|
+
user_id (str, optional): The ID of the user to send the email from. Defaults to the authenticated user.
|
|
91
|
+
cc_recipients (list[str], optional): A list of email addresses for the 'Cc' recipients.
|
|
92
|
+
bcc_recipients (list[str], optional): A list of email addresses for the 'Bcc' recipients.
|
|
93
|
+
attachments (list[dict[str, Any]], optional): A list of attachment objects. See `reply_to_email` for an example.
|
|
94
|
+
body_content_type (str, optional): The content type of the email body, e.g., "Text" or "HTML". Defaults to "Text".
|
|
95
|
+
save_to_sent_items (bool, optional): Whether to save the email to the 'Sent Items' folder. Defaults to True.
|
|
75
96
|
|
|
76
97
|
Returns:
|
|
77
|
-
Any:
|
|
98
|
+
dict[str, Any]: A dictionary confirming the send action.
|
|
78
99
|
|
|
79
100
|
Raises:
|
|
80
|
-
HTTPStatusError:
|
|
101
|
+
HTTPStatusError: If the API request fails.
|
|
102
|
+
ValueError: If the user_id cannot be retrieved.
|
|
81
103
|
|
|
82
104
|
Tags:
|
|
83
|
-
|
|
105
|
+
important
|
|
84
106
|
"""
|
|
85
|
-
# If user_id is not provided, get it automatically
|
|
86
107
|
if user_id is None:
|
|
87
|
-
user_info = self.
|
|
108
|
+
user_info = self.get_my_profile()
|
|
88
109
|
user_id = user_info.get("userPrincipalName")
|
|
89
110
|
if not user_id:
|
|
90
|
-
raise ValueError("Could not retrieve user ID from
|
|
91
|
-
|
|
111
|
+
raise ValueError("Could not retrieve user ID from get_my_profile response.")
|
|
112
|
+
|
|
113
|
+
message = {
|
|
114
|
+
"subject": subject,
|
|
115
|
+
"body": {"contentType": body_content_type, "content": body},
|
|
116
|
+
"toRecipients": [{"emailAddress": {"address": email}} for email in to_recipients],
|
|
117
|
+
}
|
|
118
|
+
if cc_recipients:
|
|
119
|
+
message["ccRecipients"] = [{"emailAddress": {"address": email}} for email in cc_recipients]
|
|
120
|
+
if bcc_recipients:
|
|
121
|
+
message["bccRecipients"] = [{"emailAddress": {"address": email}} for email in bcc_recipients]
|
|
122
|
+
if attachments:
|
|
123
|
+
message["attachments"] = attachments
|
|
124
|
+
|
|
92
125
|
request_body_data = {
|
|
93
126
|
"message": message,
|
|
94
|
-
"saveToSentItems":
|
|
127
|
+
"saveToSentItems": save_to_sent_items,
|
|
95
128
|
}
|
|
96
|
-
|
|
129
|
+
|
|
97
130
|
url = f"{self.base_url}/users/{user_id}/sendMail"
|
|
98
|
-
|
|
131
|
+
|
|
99
132
|
response = self._post(
|
|
100
133
|
url,
|
|
101
134
|
data=request_body_data,
|
|
102
|
-
params=
|
|
135
|
+
params={},
|
|
103
136
|
content_type="application/json",
|
|
104
137
|
)
|
|
105
138
|
return self._handle_response(response)
|
|
106
139
|
|
|
107
|
-
def
|
|
140
|
+
def get_email_folder(
|
|
108
141
|
self,
|
|
109
|
-
|
|
142
|
+
folder_id: str,
|
|
110
143
|
user_id: str | None = None,
|
|
111
|
-
|
|
144
|
+
include_hidden: bool | None = None,
|
|
112
145
|
select: list[str] | None = None,
|
|
113
146
|
expand: list[str] | None = None,
|
|
114
|
-
) -> Any:
|
|
147
|
+
) -> dict[str, Any]:
|
|
115
148
|
"""
|
|
116
|
-
Retrieves a specific
|
|
149
|
+
Retrieves a specific email folder's metadata by its ID.
|
|
117
150
|
|
|
118
151
|
Args:
|
|
119
|
-
|
|
120
|
-
user_id (
|
|
121
|
-
|
|
122
|
-
select (
|
|
123
|
-
expand (
|
|
152
|
+
folder_id (str): The unique identifier for the mail folder.
|
|
153
|
+
user_id (str, optional): The ID of the user who owns the folder. Defaults to the authenticated user.
|
|
154
|
+
include_hidden (bool, optional): If true, includes hidden folders in the results.
|
|
155
|
+
select (list[str], optional): A list of properties to return.
|
|
156
|
+
expand (list[str], optional): A list of related entities to expand.
|
|
124
157
|
|
|
125
158
|
Returns:
|
|
126
|
-
Any:
|
|
159
|
+
dict[str, Any]: A dictionary containing the mail folder's metadata.
|
|
127
160
|
|
|
128
161
|
Raises:
|
|
129
|
-
HTTPStatusError:
|
|
130
|
-
|
|
162
|
+
HTTPStatusError: If the API request fails.
|
|
163
|
+
ValueError: If user_id cannot be retrieved or folder_id is missing.
|
|
131
164
|
Tags:
|
|
132
|
-
|
|
165
|
+
important
|
|
133
166
|
"""
|
|
134
|
-
# If user_id is not provided, get it automatically
|
|
135
167
|
if user_id is None:
|
|
136
|
-
user_info = self.
|
|
168
|
+
user_info = self.get_my_profile()
|
|
137
169
|
user_id = user_info.get("userPrincipalName")
|
|
138
170
|
if not user_id:
|
|
139
|
-
raise ValueError("Could not retrieve user ID from
|
|
140
|
-
if
|
|
141
|
-
raise ValueError("Missing required parameter '
|
|
142
|
-
|
|
171
|
+
raise ValueError("Could not retrieve user ID from get_my_profile response.")
|
|
172
|
+
if not folder_id:
|
|
173
|
+
raise ValueError("Missing required parameter 'folder_id'.")
|
|
174
|
+
|
|
175
|
+
url = f"{self.base_url}/users/{user_id}/mailFolders/{folder_id}"
|
|
143
176
|
select_str = ",".join(select) if select else None
|
|
144
177
|
expand_str = ",".join(expand) if expand else None
|
|
145
178
|
|
|
146
179
|
query_params = {
|
|
147
180
|
k: v
|
|
148
181
|
for k, v in [
|
|
149
|
-
("includeHiddenFolders",
|
|
182
|
+
("includeHiddenFolders", include_hidden),
|
|
150
183
|
("$select", select_str),
|
|
151
184
|
("$expand", expand_str),
|
|
152
185
|
]
|
|
@@ -155,11 +188,11 @@ class OutlookApp(APIApplication):
|
|
|
155
188
|
response = self._get(url, params=query_params)
|
|
156
189
|
return self._handle_response(response)
|
|
157
190
|
|
|
158
|
-
def
|
|
191
|
+
def list_emails(
|
|
159
192
|
self,
|
|
160
193
|
user_id: str | None = None,
|
|
161
194
|
select: list[str] = ["bodyPreview"],
|
|
162
|
-
|
|
195
|
+
include_hidden: bool | None = None,
|
|
163
196
|
top: int | None = None,
|
|
164
197
|
skip: int | None = None,
|
|
165
198
|
search: str | None = None,
|
|
@@ -169,70 +202,44 @@ class OutlookApp(APIApplication):
|
|
|
169
202
|
expand: list[str] | None = None,
|
|
170
203
|
) -> dict[str, Any]:
|
|
171
204
|
"""
|
|
172
|
-
Retrieves a list of
|
|
173
|
-
|
|
174
|
-
IMPORTANT LIMITATIONS (Microsoft Graph API restrictions):
|
|
175
|
-
- `search` cannot be used with `filter`
|
|
176
|
-
- `search` cannot be used with `orderby`
|
|
177
|
-
- `search` cannot be used with `skip` (use pagination via @odata.nextLink and get_next_page instead)
|
|
178
|
-
- When using `search`, pagination uses $skiptoken instead of $skip
|
|
205
|
+
Retrieves a list of emails from a user's mailbox.
|
|
179
206
|
|
|
180
207
|
Args:
|
|
181
|
-
user_id (
|
|
182
|
-
select (list):
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
top (integer): Specify the number of items to be included in the result Example: '50'.
|
|
192
|
-
skip (integer): Specify the number of items to skip in the result. Cannot be used with 'search'. Example: '10'.
|
|
193
|
-
search (string): Search items by search phrases. Cannot be used with 'filter', 'orderby', or 'skip'.
|
|
194
|
-
filter (string): Filter items by property values. Cannot be used with 'search'.
|
|
195
|
-
count (boolean): Include count of items
|
|
196
|
-
orderby (array): Order items by property values. Cannot be used with 'search'.
|
|
197
|
-
expand (array): Expand related entities
|
|
208
|
+
user_id (str, optional): The ID of the user. Defaults to the authenticated user.
|
|
209
|
+
select (list[str], optional): A list of properties to return for each email. Defaults to ['bodyPreview'].
|
|
210
|
+
include_hidden (bool, optional): If true, includes hidden messages.
|
|
211
|
+
top (int, optional): The maximum number of emails to return.
|
|
212
|
+
skip (int, optional): The number of emails to skip. Cannot be used with 'search'.
|
|
213
|
+
search (str, optional): A search query. Cannot be used with 'filter', 'orderby', or 'skip'.
|
|
214
|
+
filter (str, optional): A filter query. Cannot be used with 'search'.
|
|
215
|
+
count (bool, optional): If true, includes the total count of emails in the response.
|
|
216
|
+
orderby (list[str], optional): A list of properties to sort the results by. Cannot be used with 'search'.
|
|
217
|
+
expand (list[str], optional): A list of related entities to expand.
|
|
198
218
|
|
|
199
219
|
Returns:
|
|
200
|
-
dict[str, Any]:
|
|
220
|
+
dict[str, Any]: A dictionary containing a list of emails and pagination information.
|
|
201
221
|
|
|
202
222
|
Raises:
|
|
203
|
-
ValueError: If incompatible parameters are used together (search with filter
|
|
204
|
-
HTTPStatusError:
|
|
205
|
-
|
|
223
|
+
ValueError: If incompatible parameters are used together (e.g., 'search' with 'filter').
|
|
224
|
+
HTTPStatusError: If the API request fails.
|
|
206
225
|
Tags:
|
|
207
|
-
|
|
226
|
+
important
|
|
208
227
|
"""
|
|
209
228
|
if search:
|
|
210
229
|
if filter:
|
|
211
|
-
raise ValueError(
|
|
212
|
-
"The 'search' parameter cannot be used together with 'filter'. "
|
|
213
|
-
"This is a Microsoft Graph API restriction. Please use either search or filter, not both."
|
|
214
|
-
)
|
|
230
|
+
raise ValueError("The 'search' parameter cannot be used with 'filter'.")
|
|
215
231
|
if orderby:
|
|
216
|
-
raise ValueError(
|
|
217
|
-
"The 'search' parameter cannot be used together with 'orderby'. "
|
|
218
|
-
"This is a Microsoft Graph API restriction. When using search, results are sorted by relevance."
|
|
219
|
-
)
|
|
232
|
+
raise ValueError("The 'search' parameter cannot be used with 'orderby'.")
|
|
220
233
|
if skip:
|
|
221
|
-
raise ValueError(
|
|
222
|
-
"The 'search' parameter cannot be used together with 'skip'. "
|
|
223
|
-
"When using search, use pagination via @odata.nextLink and the get_next_page function instead."
|
|
224
|
-
)
|
|
234
|
+
raise ValueError("The 'search' parameter cannot be used with 'skip'. Use pagination via @odata.nextLink instead.")
|
|
225
235
|
|
|
226
|
-
# If user_id is not provided, get it automatically
|
|
227
236
|
if user_id is None:
|
|
228
|
-
user_info = self.
|
|
237
|
+
user_info = self.get_my_profile()
|
|
229
238
|
user_id = user_info.get("userPrincipalName")
|
|
230
239
|
if not user_id:
|
|
231
|
-
raise ValueError("Could not retrieve user ID from
|
|
240
|
+
raise ValueError("Could not retrieve user ID from get_my_profile response.")
|
|
232
241
|
|
|
233
242
|
url = f"{self.base_url}/users/{user_id}/messages"
|
|
234
|
-
|
|
235
|
-
# Handle list parameters by joining with commas
|
|
236
243
|
select_str = ",".join(select) if select else None
|
|
237
244
|
orderby_str = ",".join(orderby) if orderby else None
|
|
238
245
|
expand_str = ",".join(expand) if expand else None
|
|
@@ -240,7 +247,7 @@ class OutlookApp(APIApplication):
|
|
|
240
247
|
query_params = {
|
|
241
248
|
k: v
|
|
242
249
|
for k, v in [
|
|
243
|
-
("includeHiddenMessages",
|
|
250
|
+
("includeHiddenMessages", include_hidden),
|
|
244
251
|
("$top", top),
|
|
245
252
|
("$skip", skip),
|
|
246
253
|
("$search", search),
|
|
@@ -256,41 +263,41 @@ class OutlookApp(APIApplication):
|
|
|
256
263
|
response = self._get(url, params=query_params)
|
|
257
264
|
return self._handle_response(response)
|
|
258
265
|
|
|
259
|
-
def
|
|
266
|
+
def get_email(
|
|
260
267
|
self,
|
|
261
268
|
message_id: str,
|
|
262
269
|
user_id: str | None = None,
|
|
263
|
-
|
|
270
|
+
include_hidden: bool | None = None,
|
|
264
271
|
select: list[str] | None = None,
|
|
265
272
|
expand: list[str] | None = None,
|
|
266
|
-
) -> Any:
|
|
273
|
+
) -> dict[str, Any]:
|
|
267
274
|
"""
|
|
268
|
-
Retrieves a specific email
|
|
275
|
+
Retrieves a specific email by its ID.
|
|
269
276
|
|
|
270
277
|
Args:
|
|
271
|
-
message_id (
|
|
272
|
-
user_id (
|
|
273
|
-
|
|
274
|
-
select (
|
|
275
|
-
expand (
|
|
278
|
+
message_id (str): The unique identifier for the email.
|
|
279
|
+
user_id (str, optional): The ID of the user who owns the email. Defaults to the authenticated user.
|
|
280
|
+
include_hidden (bool, optional): If true, includes hidden messages.
|
|
281
|
+
select (list[str], optional): A list of properties to return.
|
|
282
|
+
expand (list[str], optional): A list of related entities to expand.
|
|
276
283
|
|
|
277
284
|
Returns:
|
|
278
|
-
Any:
|
|
285
|
+
dict[str, Any]: A dictionary containing the email's details.
|
|
279
286
|
|
|
280
287
|
Raises:
|
|
281
|
-
HTTPStatusError:
|
|
282
|
-
|
|
288
|
+
HTTPStatusError: If the API request fails.
|
|
289
|
+
ValueError: If user_id cannot be retrieved or message_id is missing.
|
|
283
290
|
Tags:
|
|
284
|
-
|
|
291
|
+
important
|
|
285
292
|
"""
|
|
286
|
-
# If user_id is not provided, get it automatically
|
|
287
293
|
if user_id is None:
|
|
288
|
-
user_info = self.
|
|
294
|
+
user_info = self.get_my_profile()
|
|
289
295
|
user_id = user_info.get("userPrincipalName")
|
|
290
296
|
if not user_id:
|
|
291
|
-
raise ValueError("Could not retrieve user ID from
|
|
292
|
-
if message_id
|
|
293
|
-
raise ValueError("Missing required parameter '
|
|
297
|
+
raise ValueError("Could not retrieve user ID from get_my_profile response.")
|
|
298
|
+
if not message_id:
|
|
299
|
+
raise ValueError("Missing required parameter 'message_id'.")
|
|
300
|
+
|
|
294
301
|
url = f"{self.base_url}/users/{user_id}/messages/{message_id}"
|
|
295
302
|
select_str = ",".join(select) if select else None
|
|
296
303
|
expand_str = ",".join(expand) if expand else None
|
|
@@ -298,7 +305,7 @@ class OutlookApp(APIApplication):
|
|
|
298
305
|
query_params = {
|
|
299
306
|
k: v
|
|
300
307
|
for k, v in [
|
|
301
|
-
("includeHiddenMessages",
|
|
308
|
+
("includeHiddenMessages", include_hidden),
|
|
302
309
|
("$select", select_str),
|
|
303
310
|
("$expand", expand_str),
|
|
304
311
|
]
|
|
@@ -307,37 +314,36 @@ class OutlookApp(APIApplication):
|
|
|
307
314
|
response = self._get(url, params=query_params)
|
|
308
315
|
return self._handle_response(response)
|
|
309
316
|
|
|
310
|
-
def
|
|
317
|
+
def delete_email(self, message_id: str, user_id: str | None = None) -> dict[str, Any]:
|
|
311
318
|
"""
|
|
312
|
-
Permanently deletes a specific email
|
|
319
|
+
Permanently deletes a specific email by its ID.
|
|
313
320
|
|
|
314
321
|
Args:
|
|
315
|
-
message_id (
|
|
316
|
-
user_id (
|
|
322
|
+
message_id (str): The unique identifier for the email to be deleted.
|
|
323
|
+
user_id (str, optional): The ID of the user who owns the email. Defaults to the authenticated user.
|
|
317
324
|
|
|
318
325
|
Returns:
|
|
319
|
-
Any:
|
|
326
|
+
dict[str, Any]: A dictionary confirming the deletion.
|
|
320
327
|
|
|
321
328
|
Raises:
|
|
322
|
-
HTTPStatusError:
|
|
323
|
-
|
|
329
|
+
HTTPStatusError: If the API request fails.
|
|
330
|
+
ValueError: If user_id cannot be retrieved or message_id is missing.
|
|
324
331
|
Tags:
|
|
325
|
-
|
|
332
|
+
important
|
|
326
333
|
"""
|
|
327
|
-
# If user_id is not provided, get it automatically
|
|
328
334
|
if user_id is None:
|
|
329
|
-
user_info = self.
|
|
335
|
+
user_info = self.get_my_profile()
|
|
330
336
|
user_id = user_info.get("userPrincipalName")
|
|
331
337
|
if not user_id:
|
|
332
|
-
raise ValueError("Could not retrieve user ID from
|
|
333
|
-
if message_id
|
|
334
|
-
raise ValueError("Missing required parameter '
|
|
338
|
+
raise ValueError("Could not retrieve user ID from get_my_profile response.")
|
|
339
|
+
if not message_id:
|
|
340
|
+
raise ValueError("Missing required parameter 'message_id'.")
|
|
341
|
+
|
|
335
342
|
url = f"{self.base_url}/users/{user_id}/messages/{message_id}"
|
|
336
|
-
|
|
337
|
-
response = self._delete(url, params=query_params)
|
|
343
|
+
response = self._delete(url, params={})
|
|
338
344
|
return self._handle_response(response)
|
|
339
345
|
|
|
340
|
-
def
|
|
346
|
+
def list_email_attachments(
|
|
341
347
|
self,
|
|
342
348
|
message_id: str,
|
|
343
349
|
user_id: str | None = None,
|
|
@@ -351,60 +357,44 @@ class OutlookApp(APIApplication):
|
|
|
351
357
|
expand: list[str] | None = None,
|
|
352
358
|
) -> dict[str, Any]:
|
|
353
359
|
"""
|
|
354
|
-
Retrieves attachments for a specific email
|
|
355
|
-
|
|
356
|
-
IMPORTANT LIMITATIONS (Microsoft Graph API restrictions):
|
|
357
|
-
- `search` cannot be used with `filter`
|
|
358
|
-
- `search` cannot be used with `orderby`
|
|
359
|
-
- `search` cannot be used with `skip` (use pagination via @odata.nextLink and get_next_page instead)
|
|
360
|
+
Retrieves attachments for a specific email.
|
|
360
361
|
|
|
361
362
|
Args:
|
|
362
|
-
message_id (
|
|
363
|
-
user_id (
|
|
364
|
-
top (
|
|
365
|
-
skip (
|
|
366
|
-
search (
|
|
367
|
-
filter (
|
|
368
|
-
count (
|
|
369
|
-
orderby (
|
|
370
|
-
select (
|
|
371
|
-
expand (
|
|
363
|
+
message_id (str): The unique identifier for the email.
|
|
364
|
+
user_id (str, optional): The ID of the user who owns the email. Defaults to the authenticated user.
|
|
365
|
+
top (int, optional): The maximum number of attachments to return.
|
|
366
|
+
skip (int, optional): The number of attachments to skip. Cannot be used with 'search'.
|
|
367
|
+
search (str, optional): A search query. Cannot be used with 'filter', 'orderby', or 'skip'.
|
|
368
|
+
filter (str, optional): A filter query. Cannot be used with 'search'.
|
|
369
|
+
count (bool, optional): If true, includes the total count of attachments.
|
|
370
|
+
orderby (list[str], optional): A list of properties to sort by. Cannot be used with 'search'.
|
|
371
|
+
select (list[str], optional): A list of properties to return.
|
|
372
|
+
expand (list[str], optional): A list of related entities to expand.
|
|
372
373
|
|
|
373
374
|
Returns:
|
|
374
|
-
dict[str, Any]:
|
|
375
|
+
dict[str, Any]: A dictionary containing a list of attachments.
|
|
375
376
|
|
|
376
377
|
Raises:
|
|
377
|
-
ValueError: If incompatible parameters are used together
|
|
378
|
-
HTTPStatusError:
|
|
379
|
-
|
|
378
|
+
ValueError: If incompatible parameters are used together.
|
|
379
|
+
HTTPStatusError: If the API request fails.
|
|
380
380
|
Tags:
|
|
381
|
-
|
|
381
|
+
important
|
|
382
382
|
"""
|
|
383
383
|
if search:
|
|
384
384
|
if filter:
|
|
385
|
-
raise ValueError(
|
|
386
|
-
"The 'search' parameter cannot be used together with 'filter'. "
|
|
387
|
-
"This is a Microsoft Graph API restriction. Please use either search or filter, not both."
|
|
388
|
-
)
|
|
385
|
+
raise ValueError("The 'search' parameter cannot be used with 'filter'.")
|
|
389
386
|
if orderby:
|
|
390
|
-
raise ValueError(
|
|
391
|
-
"The 'search' parameter cannot be used together with 'orderby'. "
|
|
392
|
-
"This is a Microsoft Graph API restriction. When using search, results are sorted by relevance."
|
|
393
|
-
)
|
|
387
|
+
raise ValueError("The 'search' parameter cannot be used with 'orderby'.")
|
|
394
388
|
if skip:
|
|
395
|
-
raise ValueError(
|
|
396
|
-
"The 'search' parameter cannot be used together with 'skip'. "
|
|
397
|
-
"When using search, use pagination via @odata.nextLink and the get_next_page function instead."
|
|
398
|
-
)
|
|
389
|
+
raise ValueError("The 'search' parameter cannot be used with 'skip'. Use pagination via @odata.nextLink instead.")
|
|
399
390
|
|
|
400
|
-
# If user_id is not provided, get it automatically
|
|
401
391
|
if user_id is None:
|
|
402
|
-
user_info = self.
|
|
392
|
+
user_info = self.get_my_profile()
|
|
403
393
|
user_id = user_info.get("userPrincipalName")
|
|
404
394
|
if not user_id:
|
|
405
|
-
raise ValueError("Could not retrieve user ID from
|
|
406
|
-
if message_id
|
|
407
|
-
raise ValueError("Missing required parameter '
|
|
395
|
+
raise ValueError("Could not retrieve user ID from get_my_profile response.")
|
|
396
|
+
if not message_id:
|
|
397
|
+
raise ValueError("Missing required parameter 'message_id'.")
|
|
408
398
|
|
|
409
399
|
url = f"{self.base_url}/users/{user_id}/messages/{message_id}/attachments"
|
|
410
400
|
orderby_str = ",".join(orderby) if orderby else None
|
|
@@ -428,53 +418,107 @@ class OutlookApp(APIApplication):
|
|
|
428
418
|
response = self._get(url, params=query_params)
|
|
429
419
|
return self._handle_response(response)
|
|
430
420
|
|
|
431
|
-
def
|
|
421
|
+
def get_attachment(
|
|
432
422
|
self,
|
|
423
|
+
message_id: str,
|
|
424
|
+
attachment_id: str,
|
|
425
|
+
user_id: str | None = None,
|
|
433
426
|
) -> dict[str, Any]:
|
|
434
427
|
"""
|
|
435
|
-
|
|
428
|
+
Retrieves a specific attachment from an email message and formats it as a dictionary.
|
|
436
429
|
|
|
430
|
+
Args:
|
|
431
|
+
message_id (str): The ID of the email message.
|
|
432
|
+
attachment_id (str): The ID of the attachment.
|
|
433
|
+
user_id (str, optional): The ID of the user. Defaults to the authenticated user.
|
|
437
434
|
|
|
438
435
|
Returns:
|
|
439
|
-
dict[str, Any]:
|
|
436
|
+
dict[str, Any]: A dictionary containing the attachment details:
|
|
437
|
+
- 'type' (str): The general type of the attachment (e.g., "image", "audio", "video", "file").
|
|
438
|
+
- 'data' (str): The base64 encoded content of the attachment.
|
|
439
|
+
- 'mime_type' (str): The MIME type of the attachment.
|
|
440
|
+
- 'file_name' (str): The name of the attachment file.
|
|
441
|
+
Tags:
|
|
442
|
+
important
|
|
443
|
+
"""
|
|
444
|
+
if user_id is None:
|
|
445
|
+
user_info = self.get_my_profile()
|
|
446
|
+
user_id = user_info.get("userPrincipalName")
|
|
447
|
+
if not user_id:
|
|
448
|
+
raise ValueError("Could not retrieve user ID.")
|
|
449
|
+
if not message_id or not attachment_id:
|
|
450
|
+
raise ValueError("Missing required parameter 'message_id' or 'attachment_id'.")
|
|
440
451
|
|
|
441
|
-
|
|
442
|
-
HTTPStatusError: Raised when the API request fails with detailed error information including status code and response body.
|
|
452
|
+
url = f"{self.base_url}/users/{user_id}/messages/{message_id}/attachments/{attachment_id}"
|
|
443
453
|
|
|
444
|
-
|
|
445
|
-
|
|
454
|
+
response = self._get(url, params={})
|
|
455
|
+
attachment_data = self._handle_response(response)
|
|
456
|
+
|
|
457
|
+
content_type = attachment_data.get("contentType", "application/octet-stream")
|
|
458
|
+
attachment_type = content_type.split("/")[0] if "/" in content_type else "file"
|
|
459
|
+
if attachment_type not in ["image", "audio", "video", "text"]:
|
|
460
|
+
attachment_type = "file"
|
|
461
|
+
|
|
462
|
+
return {
|
|
463
|
+
"type": attachment_type,
|
|
464
|
+
"data": attachment_data.get("contentBytes"),
|
|
465
|
+
"mime_type": content_type,
|
|
466
|
+
"file_name": attachment_data.get("name"),
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
def get_my_profile(self) -> dict[str, Any]:
|
|
470
|
+
"""
|
|
471
|
+
Fetches the userPrincipalName for the currently authenticated user.
|
|
472
|
+
|
|
473
|
+
Returns:
|
|
474
|
+
dict[str, Any]: A dictionary containing the user's principal name.
|
|
475
|
+
|
|
476
|
+
Raises:
|
|
477
|
+
HTTPStatusError: If the API request fails.
|
|
446
478
|
"""
|
|
447
479
|
url = f"{self.base_url}/me"
|
|
448
|
-
query_params = {
|
|
449
|
-
"$select": "userPrincipalName",
|
|
450
|
-
}
|
|
480
|
+
query_params = {"$select": "userPrincipalName"}
|
|
451
481
|
response = self._get(url, params=query_params)
|
|
452
482
|
return self._handle_response(response)
|
|
453
483
|
|
|
454
|
-
def
|
|
484
|
+
def get_next_page_results(self, url: str) -> dict[str, Any]:
|
|
455
485
|
"""
|
|
456
|
-
|
|
486
|
+
Retrieves the next page of results from a paginated API response.
|
|
487
|
+
|
|
488
|
+
Args:
|
|
489
|
+
url (str): The full URL for the next page of results (@odata.nextLink).
|
|
490
|
+
|
|
491
|
+
Returns:
|
|
492
|
+
dict[str, Any]: A dictionary containing the next page of results.
|
|
493
|
+
|
|
494
|
+
Raises:
|
|
495
|
+
ValueError: If the URL is missing or invalid.
|
|
496
|
+
Tags:
|
|
497
|
+
important
|
|
457
498
|
"""
|
|
458
499
|
if not url:
|
|
459
500
|
raise ValueError("Missing required parameter 'url'.")
|
|
460
501
|
if not url.startswith(self.base_url):
|
|
461
|
-
raise ValueError(f"The provided URL
|
|
502
|
+
raise ValueError(f"The provided URL must start with '{self.base_url}'.")
|
|
503
|
+
|
|
462
504
|
relative_part = url[len(self.base_url) :]
|
|
463
505
|
parsed_relative = urlparse(relative_part)
|
|
464
506
|
path_only = parsed_relative.path
|
|
465
507
|
params = {k: v[0] for k, v in parse_qs(parsed_relative.query).items()}
|
|
508
|
+
|
|
466
509
|
response = self._get(path_only, params=params)
|
|
467
510
|
return self._handle_response(response)
|
|
468
511
|
|
|
469
512
|
def list_tools(self):
|
|
470
513
|
return [
|
|
471
|
-
self.
|
|
472
|
-
self.
|
|
473
|
-
self.
|
|
474
|
-
self.
|
|
475
|
-
self.
|
|
476
|
-
self.
|
|
477
|
-
self.
|
|
478
|
-
self.
|
|
479
|
-
self.
|
|
514
|
+
self.reply_to_email,
|
|
515
|
+
self.send_email,
|
|
516
|
+
self.get_email_folder,
|
|
517
|
+
self.list_emails,
|
|
518
|
+
self.get_email,
|
|
519
|
+
self.delete_email,
|
|
520
|
+
self.list_email_attachments,
|
|
521
|
+
self.get_attachment,
|
|
522
|
+
self.get_my_profile,
|
|
523
|
+
self.get_next_page_results,
|
|
480
524
|
]
|