universal-mcp-applications 0.1.22__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/browser_use/app.py +14 -19
- universal_mcp/applications/google_docs/app.py +363 -289
- universal_mcp/applications/google_gemini/app.py +3 -3
- universal_mcp/applications/google_sheet/app.py +6 -2
- universal_mcp/applications/linkedin/README.md +18 -4
- universal_mcp/applications/linkedin/app.py +754 -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 +281 -199
- universal_mcp/applications/reddit/app.py +30 -47
- universal_mcp/applications/scraper/README.md +7 -4
- universal_mcp/applications/scraper/app.py +310 -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/zenquotes/app.py +2 -2
- {universal_mcp_applications-0.1.22.dist-info → universal_mcp_applications-0.1.33.dist-info}/METADATA +2 -2
- {universal_mcp_applications-0.1.22.dist-info → universal_mcp_applications-0.1.33.dist-info}/RECORD +21 -21
- 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.33.dist-info}/WHEEL +0 -0
- {universal_mcp_applications-0.1.22.dist-info → universal_mcp_applications-0.1.33.dist-info}/licenses/LICENSE +0 -0
|
@@ -10,163 +10,189 @@ 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
|
-
|
|
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(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"message": message,
|
|
52
|
-
}
|
|
53
|
-
request_body_data = {
|
|
54
|
-
k: v for k, v in request_body_data.items() if v is not None
|
|
55
|
-
}
|
|
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
|
+
|
|
56
61
|
url = f"{self.base_url}/users/{user_id}/messages/{message_id}/reply"
|
|
57
|
-
|
|
62
|
+
|
|
58
63
|
response = self._post(
|
|
59
64
|
url,
|
|
60
65
|
data=request_body_data,
|
|
61
|
-
params=
|
|
66
|
+
params={},
|
|
62
67
|
content_type="application/json",
|
|
63
68
|
)
|
|
64
69
|
return self._handle_response(response)
|
|
65
70
|
|
|
66
|
-
def
|
|
71
|
+
def send_email(
|
|
67
72
|
self,
|
|
68
|
-
|
|
73
|
+
subject: str,
|
|
74
|
+
body: str,
|
|
75
|
+
to_recipients: list[str],
|
|
69
76
|
user_id: str | None = None,
|
|
70
|
-
|
|
71
|
-
|
|
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]:
|
|
72
83
|
"""
|
|
73
|
-
Sends a new email
|
|
84
|
+
Sends a new email.
|
|
74
85
|
|
|
75
86
|
Args:
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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.
|
|
79
96
|
|
|
80
97
|
Returns:
|
|
81
|
-
Any:
|
|
98
|
+
dict[str, Any]: A dictionary confirming the send action.
|
|
82
99
|
|
|
83
100
|
Raises:
|
|
84
|
-
HTTPStatusError:
|
|
101
|
+
HTTPStatusError: If the API request fails.
|
|
102
|
+
ValueError: If the user_id cannot be retrieved.
|
|
85
103
|
|
|
86
104
|
Tags:
|
|
87
|
-
|
|
105
|
+
important
|
|
88
106
|
"""
|
|
89
|
-
# If user_id is not provided, get it automatically
|
|
90
107
|
if user_id is None:
|
|
91
|
-
user_info = self.
|
|
108
|
+
user_info = self.get_my_profile()
|
|
92
109
|
user_id = user_info.get("userPrincipalName")
|
|
93
110
|
if not user_id:
|
|
94
|
-
raise ValueError(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
"
|
|
100
|
-
"saveToSentItems": saveToSentItems,
|
|
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],
|
|
101
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
|
+
|
|
102
125
|
request_body_data = {
|
|
103
|
-
|
|
126
|
+
"message": message,
|
|
127
|
+
"saveToSentItems": save_to_sent_items,
|
|
104
128
|
}
|
|
129
|
+
|
|
105
130
|
url = f"{self.base_url}/users/{user_id}/sendMail"
|
|
106
|
-
|
|
131
|
+
|
|
107
132
|
response = self._post(
|
|
108
133
|
url,
|
|
109
134
|
data=request_body_data,
|
|
110
|
-
params=
|
|
135
|
+
params={},
|
|
111
136
|
content_type="application/json",
|
|
112
137
|
)
|
|
113
138
|
return self._handle_response(response)
|
|
114
139
|
|
|
115
|
-
def
|
|
140
|
+
def get_email_folder(
|
|
116
141
|
self,
|
|
117
|
-
|
|
142
|
+
folder_id: str,
|
|
118
143
|
user_id: str | None = None,
|
|
119
|
-
|
|
144
|
+
include_hidden: bool | None = None,
|
|
120
145
|
select: list[str] | None = None,
|
|
121
146
|
expand: list[str] | None = None,
|
|
122
|
-
) -> Any:
|
|
147
|
+
) -> dict[str, Any]:
|
|
123
148
|
"""
|
|
124
|
-
Retrieves a specific
|
|
149
|
+
Retrieves a specific email folder's metadata by its ID.
|
|
125
150
|
|
|
126
151
|
Args:
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
select (
|
|
131
|
-
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.
|
|
132
157
|
|
|
133
158
|
Returns:
|
|
134
|
-
Any:
|
|
159
|
+
dict[str, Any]: A dictionary containing the mail folder's metadata.
|
|
135
160
|
|
|
136
161
|
Raises:
|
|
137
|
-
HTTPStatusError:
|
|
138
|
-
|
|
162
|
+
HTTPStatusError: If the API request fails.
|
|
163
|
+
ValueError: If user_id cannot be retrieved or folder_id is missing.
|
|
139
164
|
Tags:
|
|
140
|
-
|
|
165
|
+
important
|
|
141
166
|
"""
|
|
142
|
-
# If user_id is not provided, get it automatically
|
|
143
167
|
if user_id is None:
|
|
144
|
-
user_info = self.
|
|
168
|
+
user_info = self.get_my_profile()
|
|
145
169
|
user_id = user_info.get("userPrincipalName")
|
|
146
170
|
if not user_id:
|
|
147
|
-
raise ValueError(
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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}"
|
|
176
|
+
select_str = ",".join(select) if select else None
|
|
177
|
+
expand_str = ",".join(expand) if expand else None
|
|
178
|
+
|
|
153
179
|
query_params = {
|
|
154
180
|
k: v
|
|
155
181
|
for k, v in [
|
|
156
|
-
("includeHiddenFolders",
|
|
157
|
-
("$select",
|
|
158
|
-
("$expand",
|
|
182
|
+
("includeHiddenFolders", include_hidden),
|
|
183
|
+
("$select", select_str),
|
|
184
|
+
("$expand", expand_str),
|
|
159
185
|
]
|
|
160
186
|
if v is not None
|
|
161
187
|
}
|
|
162
188
|
response = self._get(url, params=query_params)
|
|
163
189
|
return self._handle_response(response)
|
|
164
190
|
|
|
165
|
-
def
|
|
191
|
+
def list_emails(
|
|
166
192
|
self,
|
|
167
193
|
user_id: str | None = None,
|
|
168
194
|
select: list[str] = ["bodyPreview"],
|
|
169
|
-
|
|
195
|
+
include_hidden: bool | None = None,
|
|
170
196
|
top: int | None = None,
|
|
171
197
|
skip: int | None = None,
|
|
172
198
|
search: str | None = None,
|
|
@@ -176,48 +202,44 @@ class OutlookApp(APIApplication):
|
|
|
176
202
|
expand: list[str] | None = None,
|
|
177
203
|
) -> dict[str, Any]:
|
|
178
204
|
"""
|
|
179
|
-
Retrieves a list of
|
|
205
|
+
Retrieves a list of emails from a user's mailbox.
|
|
180
206
|
|
|
181
207
|
Args:
|
|
182
|
-
user_id (
|
|
183
|
-
select (list):
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
top (integer): Specify the number of items to be included in the result Example: '50'.
|
|
193
|
-
skip (integer): Specify the number of items to skip in the result Example: '10'.
|
|
194
|
-
search (string): Search items by search phrases
|
|
195
|
-
filter (string): Filter items by property values
|
|
196
|
-
count (boolean): Include count of items
|
|
197
|
-
orderby (array): Order items by property values
|
|
198
|
-
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.
|
|
199
218
|
|
|
200
219
|
Returns:
|
|
201
|
-
dict[str, Any]:
|
|
220
|
+
dict[str, Any]: A dictionary containing a list of emails and pagination information.
|
|
202
221
|
|
|
203
222
|
Raises:
|
|
204
|
-
|
|
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:
|
|
229
|
+
if filter:
|
|
230
|
+
raise ValueError("The 'search' parameter cannot be used with 'filter'.")
|
|
231
|
+
if orderby:
|
|
232
|
+
raise ValueError("The 'search' parameter cannot be used with 'orderby'.")
|
|
233
|
+
if skip:
|
|
234
|
+
raise ValueError("The 'search' parameter cannot be used with 'skip'. Use pagination via @odata.nextLink instead.")
|
|
235
|
+
|
|
210
236
|
if user_id is None:
|
|
211
|
-
user_info = self.
|
|
237
|
+
user_info = self.get_my_profile()
|
|
212
238
|
user_id = user_info.get("userPrincipalName")
|
|
213
239
|
if not user_id:
|
|
214
|
-
raise ValueError(
|
|
215
|
-
"Could not retrieve user ID from get_current_user_profile response."
|
|
216
|
-
)
|
|
240
|
+
raise ValueError("Could not retrieve user ID from get_my_profile response.")
|
|
217
241
|
|
|
218
242
|
url = f"{self.base_url}/users/{user_id}/messages"
|
|
219
|
-
|
|
220
|
-
# Handle list parameters by joining with commas
|
|
221
243
|
select_str = ",".join(select) if select else None
|
|
222
244
|
orderby_str = ",".join(orderby) if orderby else None
|
|
223
245
|
expand_str = ",".join(expand) if expand else None
|
|
@@ -225,7 +247,7 @@ class OutlookApp(APIApplication):
|
|
|
225
247
|
query_params = {
|
|
226
248
|
k: v
|
|
227
249
|
for k, v in [
|
|
228
|
-
("includeHiddenMessages",
|
|
250
|
+
("includeHiddenMessages", include_hidden),
|
|
229
251
|
("$top", top),
|
|
230
252
|
("$skip", skip),
|
|
231
253
|
("$search", search),
|
|
@@ -241,89 +263,87 @@ class OutlookApp(APIApplication):
|
|
|
241
263
|
response = self._get(url, params=query_params)
|
|
242
264
|
return self._handle_response(response)
|
|
243
265
|
|
|
244
|
-
def
|
|
266
|
+
def get_email(
|
|
245
267
|
self,
|
|
246
268
|
message_id: str,
|
|
247
269
|
user_id: str | None = None,
|
|
248
|
-
|
|
270
|
+
include_hidden: bool | None = None,
|
|
249
271
|
select: list[str] | None = None,
|
|
250
272
|
expand: list[str] | None = None,
|
|
251
|
-
) -> Any:
|
|
273
|
+
) -> dict[str, Any]:
|
|
252
274
|
"""
|
|
253
|
-
Retrieves a specific email
|
|
275
|
+
Retrieves a specific email by its ID.
|
|
254
276
|
|
|
255
277
|
Args:
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
select (
|
|
260
|
-
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.
|
|
261
283
|
|
|
262
284
|
Returns:
|
|
263
|
-
Any:
|
|
285
|
+
dict[str, Any]: A dictionary containing the email's details.
|
|
264
286
|
|
|
265
287
|
Raises:
|
|
266
|
-
HTTPStatusError:
|
|
267
|
-
|
|
288
|
+
HTTPStatusError: If the API request fails.
|
|
289
|
+
ValueError: If user_id cannot be retrieved or message_id is missing.
|
|
268
290
|
Tags:
|
|
269
|
-
|
|
291
|
+
important
|
|
270
292
|
"""
|
|
271
|
-
# If user_id is not provided, get it automatically
|
|
272
293
|
if user_id is None:
|
|
273
|
-
user_info = self.
|
|
294
|
+
user_info = self.get_my_profile()
|
|
274
295
|
user_id = user_info.get("userPrincipalName")
|
|
275
296
|
if not user_id:
|
|
276
|
-
raise ValueError(
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
raise ValueError("Missing required parameter 'message-id'.")
|
|
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
|
+
|
|
281
301
|
url = f"{self.base_url}/users/{user_id}/messages/{message_id}"
|
|
302
|
+
select_str = ",".join(select) if select else None
|
|
303
|
+
expand_str = ",".join(expand) if expand else None
|
|
304
|
+
|
|
282
305
|
query_params = {
|
|
283
306
|
k: v
|
|
284
307
|
for k, v in [
|
|
285
|
-
("includeHiddenMessages",
|
|
286
|
-
("$select",
|
|
287
|
-
("$expand",
|
|
308
|
+
("includeHiddenMessages", include_hidden),
|
|
309
|
+
("$select", select_str),
|
|
310
|
+
("$expand", expand_str),
|
|
288
311
|
]
|
|
289
312
|
if v is not None
|
|
290
313
|
}
|
|
291
314
|
response = self._get(url, params=query_params)
|
|
292
315
|
return self._handle_response(response)
|
|
293
316
|
|
|
294
|
-
def
|
|
317
|
+
def delete_email(self, message_id: str, user_id: str | None = None) -> dict[str, Any]:
|
|
295
318
|
"""
|
|
296
|
-
Permanently deletes a specific email
|
|
319
|
+
Permanently deletes a specific email by its ID.
|
|
297
320
|
|
|
298
321
|
Args:
|
|
299
|
-
|
|
300
|
-
|
|
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.
|
|
301
324
|
|
|
302
325
|
Returns:
|
|
303
|
-
Any:
|
|
326
|
+
dict[str, Any]: A dictionary confirming the deletion.
|
|
304
327
|
|
|
305
328
|
Raises:
|
|
306
|
-
HTTPStatusError:
|
|
307
|
-
|
|
329
|
+
HTTPStatusError: If the API request fails.
|
|
330
|
+
ValueError: If user_id cannot be retrieved or message_id is missing.
|
|
308
331
|
Tags:
|
|
309
|
-
|
|
332
|
+
important
|
|
310
333
|
"""
|
|
311
|
-
# If user_id is not provided, get it automatically
|
|
312
334
|
if user_id is None:
|
|
313
|
-
user_info = self.
|
|
335
|
+
user_info = self.get_my_profile()
|
|
314
336
|
user_id = user_info.get("userPrincipalName")
|
|
315
337
|
if not user_id:
|
|
316
|
-
raise ValueError(
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
raise ValueError("Missing required parameter 'message-id'.")
|
|
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
|
+
|
|
321
342
|
url = f"{self.base_url}/users/{user_id}/messages/{message_id}"
|
|
322
|
-
|
|
323
|
-
response = self._delete(url, params=query_params)
|
|
343
|
+
response = self._delete(url, params={})
|
|
324
344
|
return self._handle_response(response)
|
|
325
345
|
|
|
326
|
-
def
|
|
346
|
+
def list_email_attachments(
|
|
327
347
|
self,
|
|
328
348
|
message_id: str,
|
|
329
349
|
user_id: str | None = None,
|
|
@@ -337,40 +357,50 @@ class OutlookApp(APIApplication):
|
|
|
337
357
|
expand: list[str] | None = None,
|
|
338
358
|
) -> dict[str, Any]:
|
|
339
359
|
"""
|
|
340
|
-
Retrieves attachments for a specific email
|
|
360
|
+
Retrieves attachments for a specific email.
|
|
341
361
|
|
|
342
362
|
Args:
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
top (
|
|
346
|
-
skip (
|
|
347
|
-
search (
|
|
348
|
-
filter (
|
|
349
|
-
count (
|
|
350
|
-
orderby (
|
|
351
|
-
select (
|
|
352
|
-
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.
|
|
353
373
|
|
|
354
374
|
Returns:
|
|
355
|
-
dict[str, Any]:
|
|
375
|
+
dict[str, Any]: A dictionary containing a list of attachments.
|
|
356
376
|
|
|
357
377
|
Raises:
|
|
358
|
-
|
|
359
|
-
|
|
378
|
+
ValueError: If incompatible parameters are used together.
|
|
379
|
+
HTTPStatusError: If the API request fails.
|
|
360
380
|
Tags:
|
|
361
|
-
|
|
381
|
+
important
|
|
362
382
|
"""
|
|
363
|
-
|
|
383
|
+
if search:
|
|
384
|
+
if filter:
|
|
385
|
+
raise ValueError("The 'search' parameter cannot be used with 'filter'.")
|
|
386
|
+
if orderby:
|
|
387
|
+
raise ValueError("The 'search' parameter cannot be used with 'orderby'.")
|
|
388
|
+
if skip:
|
|
389
|
+
raise ValueError("The 'search' parameter cannot be used with 'skip'. Use pagination via @odata.nextLink instead.")
|
|
390
|
+
|
|
364
391
|
if user_id is None:
|
|
365
|
-
user_info = self.
|
|
392
|
+
user_info = self.get_my_profile()
|
|
366
393
|
user_id = user_info.get("userPrincipalName")
|
|
367
394
|
if not user_id:
|
|
368
|
-
raise ValueError(
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
raise ValueError("Missing required parameter 'message-id'.")
|
|
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'.")
|
|
398
|
+
|
|
373
399
|
url = f"{self.base_url}/users/{user_id}/messages/{message_id}/attachments"
|
|
400
|
+
orderby_str = ",".join(orderby) if orderby else None
|
|
401
|
+
select_str = ",".join(select) if select else None
|
|
402
|
+
expand_str = ",".join(expand) if expand else None
|
|
403
|
+
|
|
374
404
|
query_params = {
|
|
375
405
|
k: v
|
|
376
406
|
for k, v in [
|
|
@@ -379,64 +409,116 @@ class OutlookApp(APIApplication):
|
|
|
379
409
|
("$search", search),
|
|
380
410
|
("$filter", filter),
|
|
381
411
|
("$count", count),
|
|
382
|
-
("$orderby",
|
|
383
|
-
("$select",
|
|
384
|
-
("$expand",
|
|
412
|
+
("$orderby", orderby_str),
|
|
413
|
+
("$select", select_str),
|
|
414
|
+
("$expand", expand_str),
|
|
385
415
|
]
|
|
386
416
|
if v is not None
|
|
387
417
|
}
|
|
388
418
|
response = self._get(url, params=query_params)
|
|
389
419
|
return self._handle_response(response)
|
|
390
420
|
|
|
391
|
-
def
|
|
421
|
+
def get_attachment(
|
|
392
422
|
self,
|
|
423
|
+
message_id: str,
|
|
424
|
+
attachment_id: str,
|
|
425
|
+
user_id: str | None = None,
|
|
393
426
|
) -> dict[str, Any]:
|
|
394
427
|
"""
|
|
395
|
-
|
|
428
|
+
Retrieves a specific attachment from an email message and formats it as a dictionary.
|
|
396
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.
|
|
397
434
|
|
|
398
435
|
Returns:
|
|
399
|
-
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'.")
|
|
400
451
|
|
|
401
|
-
|
|
402
|
-
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}"
|
|
403
453
|
|
|
404
|
-
|
|
405
|
-
|
|
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.
|
|
406
478
|
"""
|
|
407
479
|
url = f"{self.base_url}/me"
|
|
408
|
-
query_params = {
|
|
409
|
-
"$select": "userPrincipalName",
|
|
410
|
-
}
|
|
480
|
+
query_params = {"$select": "userPrincipalName"}
|
|
411
481
|
response = self._get(url, params=query_params)
|
|
412
482
|
return self._handle_response(response)
|
|
413
483
|
|
|
414
|
-
def
|
|
484
|
+
def get_next_page_results(self, url: str) -> dict[str, Any]:
|
|
415
485
|
"""
|
|
416
|
-
|
|
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
|
|
417
498
|
"""
|
|
418
499
|
if not url:
|
|
419
500
|
raise ValueError("Missing required parameter 'url'.")
|
|
420
501
|
if not url.startswith(self.base_url):
|
|
421
|
-
raise ValueError(
|
|
422
|
-
|
|
423
|
-
)
|
|
502
|
+
raise ValueError(f"The provided URL must start with '{self.base_url}'.")
|
|
503
|
+
|
|
424
504
|
relative_part = url[len(self.base_url) :]
|
|
425
505
|
parsed_relative = urlparse(relative_part)
|
|
426
506
|
path_only = parsed_relative.path
|
|
427
507
|
params = {k: v[0] for k, v in parse_qs(parsed_relative.query).items()}
|
|
508
|
+
|
|
428
509
|
response = self._get(path_only, params=params)
|
|
429
510
|
return self._handle_response(response)
|
|
430
511
|
|
|
431
512
|
def list_tools(self):
|
|
432
513
|
return [
|
|
433
|
-
self.
|
|
434
|
-
self.
|
|
435
|
-
self.
|
|
436
|
-
self.
|
|
437
|
-
self.
|
|
438
|
-
self.
|
|
439
|
-
self.
|
|
440
|
-
self.
|
|
441
|
-
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,
|
|
442
524
|
]
|