mcp-sharepoint-us 2.0.13__py3-none-any.whl → 2.0.14__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 mcp-sharepoint-us might be problematic. Click here for more details.
- mcp_sharepoint/__init__.py +37 -13
- mcp_sharepoint/auth.py +9 -1
- mcp_sharepoint/graph_api.py +130 -64
- {mcp_sharepoint_us-2.0.13.dist-info → mcp_sharepoint_us-2.0.14.dist-info}/METADATA +1 -1
- mcp_sharepoint_us-2.0.14.dist-info/RECORD +10 -0
- mcp_sharepoint_us-2.0.13.dist-info/RECORD +0 -10
- {mcp_sharepoint_us-2.0.13.dist-info → mcp_sharepoint_us-2.0.14.dist-info}/WHEEL +0 -0
- {mcp_sharepoint_us-2.0.13.dist-info → mcp_sharepoint_us-2.0.14.dist-info}/entry_points.txt +0 -0
- {mcp_sharepoint_us-2.0.13.dist-info → mcp_sharepoint_us-2.0.14.dist-info}/licenses/LICENSE +0 -0
- {mcp_sharepoint_us-2.0.13.dist-info → mcp_sharepoint_us-2.0.14.dist-info}/top_level.txt +0 -0
mcp_sharepoint/__init__.py
CHANGED
|
@@ -35,6 +35,7 @@ def ensure_context(func):
|
|
|
35
35
|
global graph_client, authenticator
|
|
36
36
|
if graph_client is None:
|
|
37
37
|
try:
|
|
38
|
+
logger.info("Initializing Graph API client...")
|
|
38
39
|
from .auth import SharePointAuthenticator
|
|
39
40
|
|
|
40
41
|
# Get credentials
|
|
@@ -44,6 +45,11 @@ def ensure_context(func):
|
|
|
44
45
|
tenant_id = os.getenv("SHP_TENANT_ID")
|
|
45
46
|
cloud = "government" if ".sharepoint.us" in site_url else "commercial"
|
|
46
47
|
|
|
48
|
+
logger.info(f"Site URL: {site_url}")
|
|
49
|
+
logger.info(f"Tenant ID: {tenant_id}")
|
|
50
|
+
logger.info(f"Client ID: {client_id}")
|
|
51
|
+
logger.info(f"Cloud: {cloud}")
|
|
52
|
+
|
|
47
53
|
# Create shared authenticator
|
|
48
54
|
authenticator = SharePointAuthenticator(
|
|
49
55
|
site_url=site_url,
|
|
@@ -52,11 +58,15 @@ def ensure_context(func):
|
|
|
52
58
|
tenant_id=tenant_id,
|
|
53
59
|
cloud=cloud
|
|
54
60
|
)
|
|
61
|
+
logger.info("Authenticator created successfully")
|
|
55
62
|
|
|
56
63
|
# Create Graph API client with direct token access
|
|
57
64
|
def get_token():
|
|
58
65
|
"""Get access token for Graph API"""
|
|
59
|
-
|
|
66
|
+
logger.debug("Token callback invoked")
|
|
67
|
+
token = authenticator.get_access_token()
|
|
68
|
+
logger.debug(f"Token acquired (length: {len(token)})")
|
|
69
|
+
return token
|
|
60
70
|
|
|
61
71
|
graph_client = GraphAPIClient(
|
|
62
72
|
site_url=site_url,
|
|
@@ -65,7 +75,7 @@ def ensure_context(func):
|
|
|
65
75
|
logger.info("Graph API client initialized successfully")
|
|
66
76
|
|
|
67
77
|
except Exception as e:
|
|
68
|
-
logger.error(f"Failed to initialize Graph API client: {e}")
|
|
78
|
+
logger.error(f"Failed to initialize Graph API client: {e}", exc_info=True)
|
|
69
79
|
raise RuntimeError(
|
|
70
80
|
f"Graph API authentication failed: {e}. "
|
|
71
81
|
"Please check your environment variables and ensure:\n"
|
|
@@ -321,28 +331,42 @@ async def call_tool(name: str, arguments: dict) -> list[TextContent]:
|
|
|
321
331
|
|
|
322
332
|
|
|
323
333
|
async def test_connection() -> list[TextContent]:
|
|
324
|
-
"""Test SharePoint connection"""
|
|
334
|
+
"""Test SharePoint connection using Microsoft Graph API"""
|
|
325
335
|
try:
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
336
|
+
logger.info("Testing Graph API connection...")
|
|
337
|
+
|
|
338
|
+
# Try to get site ID and drive ID
|
|
339
|
+
site_id = await asyncio.to_thread(graph_client._get_site_id)
|
|
340
|
+
drive_id = await asyncio.to_thread(graph_client._get_drive_id)
|
|
341
|
+
|
|
342
|
+
auth_method = "msal (Microsoft Graph API)"
|
|
343
|
+
|
|
344
|
+
logger.info(f"✓ Connection test successful - Site ID: {site_id}, Drive ID: {drive_id}")
|
|
345
|
+
|
|
329
346
|
return [TextContent(
|
|
330
347
|
type="text",
|
|
331
|
-
text=f"✓ Successfully connected to SharePoint!\n\n"
|
|
332
|
-
f"Site
|
|
333
|
-
f"
|
|
334
|
-
f"
|
|
348
|
+
text=f"✓ Successfully connected to SharePoint via Microsoft Graph API!\n\n"
|
|
349
|
+
f"Site URL: {graph_client.site_url}\n"
|
|
350
|
+
f"Graph Endpoint: {graph_client.graph_endpoint}\n"
|
|
351
|
+
f"Site ID: {site_id}\n"
|
|
352
|
+
f"Drive ID: {drive_id}\n"
|
|
353
|
+
f"Authentication Method: {auth_method}\n"
|
|
335
354
|
f"Tenant ID: {os.getenv('SHP_TENANT_ID')}\n\n"
|
|
336
|
-
f"Connection is working correctly with
|
|
355
|
+
f"Connection is working correctly with Microsoft Graph API."
|
|
337
356
|
)]
|
|
338
357
|
except Exception as e:
|
|
358
|
+
logger.error(f"✗ Connection test failed: {str(e)}", exc_info=True)
|
|
339
359
|
return [TextContent(
|
|
340
360
|
type="text",
|
|
341
361
|
text=f"✗ Connection failed: {str(e)}\n\n"
|
|
342
362
|
f"This usually means:\n"
|
|
343
363
|
f"1. Your credentials are incorrect\n"
|
|
344
|
-
f"2. Your app doesn't have proper
|
|
345
|
-
f"3.
|
|
364
|
+
f"2. Your app doesn't have proper Microsoft Graph permissions\n"
|
|
365
|
+
f"3. Network connectivity issues\n"
|
|
366
|
+
f"4. Azure AD app registration is missing required permissions:\n"
|
|
367
|
+
f" - Sites.Read.All\n"
|
|
368
|
+
f" - Files.ReadWrite.All\n\n"
|
|
369
|
+
f"Check the logs for more details."
|
|
346
370
|
)]
|
|
347
371
|
|
|
348
372
|
|
mcp_sharepoint/auth.py
CHANGED
|
@@ -226,16 +226,22 @@ class SharePointAuthenticator:
|
|
|
226
226
|
|
|
227
227
|
now = int(time.time())
|
|
228
228
|
if self._access_token and now < (self._access_token_exp - 60):
|
|
229
|
+
logger.debug("Using cached access token")
|
|
229
230
|
return self._access_token
|
|
230
231
|
|
|
232
|
+
logger.info(f"Acquiring new access token from {self._authority_url}")
|
|
233
|
+
logger.debug(f"Scopes: {self._scopes}")
|
|
234
|
+
|
|
231
235
|
last_err = None
|
|
232
236
|
for attempt in range(1, 6): # 5 attempts
|
|
233
237
|
try:
|
|
238
|
+
logger.debug(f"Token acquisition attempt {attempt}/5")
|
|
234
239
|
result = self._msal_app.acquire_token_for_client(scopes=self._scopes)
|
|
235
240
|
|
|
236
241
|
if "access_token" not in result:
|
|
237
242
|
error_desc = result.get("error_description", "Unknown error")
|
|
238
243
|
error = result.get("error", "Unknown")
|
|
244
|
+
logger.error(f"Token acquisition failed: {error} - {error_desc}")
|
|
239
245
|
raise ValueError(
|
|
240
246
|
f"Failed to acquire token: {error} - {error_desc}\n"
|
|
241
247
|
f"Authority: {self._authority_url}\n"
|
|
@@ -249,11 +255,13 @@ class SharePointAuthenticator:
|
|
|
249
255
|
self._access_token = token
|
|
250
256
|
self._access_token_exp = int(time.time()) + expires_in
|
|
251
257
|
|
|
252
|
-
logger.info(f"Successfully acquired Graph API token")
|
|
258
|
+
logger.info(f"Successfully acquired Graph API token (expires in {expires_in}s)")
|
|
259
|
+
logger.debug(f"Token length: {len(token)}, starts with: {token[:20]}...")
|
|
253
260
|
return token
|
|
254
261
|
|
|
255
262
|
except Exception as e:
|
|
256
263
|
last_err = e
|
|
264
|
+
logger.error(f"Token acquisition attempt {attempt}/5 failed: {type(e).__name__}: {e}")
|
|
257
265
|
# Exponential backoff with jitter
|
|
258
266
|
sleep_s = min(8.0, (2 ** (attempt - 1)) * 0.5) + random.random() * 0.25
|
|
259
267
|
logger.warning(
|
mcp_sharepoint/graph_api.py
CHANGED
|
@@ -42,6 +42,7 @@ class GraphAPIClient:
|
|
|
42
42
|
|
|
43
43
|
def _get_headers(self) -> Dict[str, str]:
|
|
44
44
|
"""Get authorization headers with access token."""
|
|
45
|
+
logger.debug("Getting authorization headers...")
|
|
45
46
|
token_obj = self.token_callback()
|
|
46
47
|
# Handle both TokenResponse objects and plain strings
|
|
47
48
|
if hasattr(token_obj, 'accessToken'):
|
|
@@ -49,6 +50,8 @@ class GraphAPIClient:
|
|
|
49
50
|
else:
|
|
50
51
|
token = str(token_obj)
|
|
51
52
|
|
|
53
|
+
logger.debug(f"Token acquired for headers (length: {len(token)}, starts with: {token[:20]}...)")
|
|
54
|
+
|
|
52
55
|
return {
|
|
53
56
|
"Authorization": f"Bearer {token}",
|
|
54
57
|
"Accept": "application/json",
|
|
@@ -91,6 +94,7 @@ class GraphAPIClient:
|
|
|
91
94
|
Caches the result for reuse.
|
|
92
95
|
"""
|
|
93
96
|
if self._site_id:
|
|
97
|
+
logger.debug(f"Using cached site ID: {self._site_id}")
|
|
94
98
|
return self._site_id
|
|
95
99
|
|
|
96
100
|
parsed = urlparse(self.site_url)
|
|
@@ -104,12 +108,18 @@ class GraphAPIClient:
|
|
|
104
108
|
else:
|
|
105
109
|
url = f"{self.graph_endpoint}/sites/{hostname}:/{path}"
|
|
106
110
|
|
|
107
|
-
|
|
108
|
-
|
|
111
|
+
logger.info(f"Fetching site ID from: {url}")
|
|
112
|
+
try:
|
|
113
|
+
response = requests.get(url, headers=self._get_headers(), timeout=30)
|
|
114
|
+
logger.debug(f"Response status: {response.status_code}")
|
|
115
|
+
self._handle_response(response)
|
|
109
116
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
117
|
+
self._site_id = response.json()["id"]
|
|
118
|
+
logger.info(f"Retrieved site ID: {self._site_id}")
|
|
119
|
+
return self._site_id
|
|
120
|
+
except requests.exceptions.RequestException as e:
|
|
121
|
+
logger.error(f"Network error getting site ID: {type(e).__name__}: {e}", exc_info=True)
|
|
122
|
+
raise
|
|
113
123
|
|
|
114
124
|
def _get_drive_id(self) -> str:
|
|
115
125
|
"""
|
|
@@ -117,17 +127,24 @@ class GraphAPIClient:
|
|
|
117
127
|
Caches the result for reuse.
|
|
118
128
|
"""
|
|
119
129
|
if self._drive_id:
|
|
130
|
+
logger.debug(f"Using cached drive ID: {self._drive_id}")
|
|
120
131
|
return self._drive_id
|
|
121
132
|
|
|
122
133
|
site_id = self._get_site_id()
|
|
123
134
|
url = f"{self.graph_endpoint}/sites/{site_id}/drive"
|
|
124
135
|
|
|
125
|
-
|
|
126
|
-
|
|
136
|
+
logger.info(f"Fetching drive ID from: {url}")
|
|
137
|
+
try:
|
|
138
|
+
response = requests.get(url, headers=self._get_headers(), timeout=30)
|
|
139
|
+
logger.debug(f"Response status: {response.status_code}")
|
|
140
|
+
self._handle_response(response)
|
|
127
141
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
142
|
+
self._drive_id = response.json()["id"]
|
|
143
|
+
logger.info(f"Retrieved drive ID: {self._drive_id}")
|
|
144
|
+
return self._drive_id
|
|
145
|
+
except requests.exceptions.RequestException as e:
|
|
146
|
+
logger.error(f"Network error getting drive ID: {type(e).__name__}: {e}", exc_info=True)
|
|
147
|
+
raise
|
|
131
148
|
|
|
132
149
|
def list_folders(self, folder_path: str = "") -> List[Dict[str, Any]]:
|
|
133
150
|
"""
|
|
@@ -139,6 +156,7 @@ class GraphAPIClient:
|
|
|
139
156
|
Returns:
|
|
140
157
|
List of folder objects with name, id, webUrl
|
|
141
158
|
"""
|
|
159
|
+
logger.info(f"Listing folders in '{folder_path}'")
|
|
142
160
|
site_id = self._get_site_id()
|
|
143
161
|
drive_id = self._get_drive_id()
|
|
144
162
|
|
|
@@ -149,23 +167,29 @@ class GraphAPIClient:
|
|
|
149
167
|
else:
|
|
150
168
|
url = f"{self.graph_endpoint}/sites/{site_id}/drives/{drive_id}/root/children"
|
|
151
169
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
170
|
+
logger.info(f"Fetching folders from: {url}")
|
|
171
|
+
try:
|
|
172
|
+
response = requests.get(url, headers=self._get_headers(), timeout=30)
|
|
173
|
+
logger.debug(f"Response status: {response.status_code}")
|
|
174
|
+
self._handle_response(response)
|
|
175
|
+
|
|
176
|
+
items = response.json().get("value", [])
|
|
177
|
+
# Filter to only folders
|
|
178
|
+
folders = [
|
|
179
|
+
{
|
|
180
|
+
"name": item["name"],
|
|
181
|
+
"id": item["id"],
|
|
182
|
+
"webUrl": item.get("webUrl", ""),
|
|
183
|
+
}
|
|
184
|
+
for item in items
|
|
185
|
+
if "folder" in item
|
|
186
|
+
]
|
|
187
|
+
|
|
188
|
+
logger.info(f"Found {len(folders)} folders in '{folder_path}'")
|
|
189
|
+
return folders
|
|
190
|
+
except requests.exceptions.RequestException as e:
|
|
191
|
+
logger.error(f"Network error listing folders: {type(e).__name__}: {e}", exc_info=True)
|
|
192
|
+
raise
|
|
169
193
|
|
|
170
194
|
def list_documents(self, folder_path: str = "") -> List[Dict[str, Any]]:
|
|
171
195
|
"""
|
|
@@ -177,6 +201,7 @@ class GraphAPIClient:
|
|
|
177
201
|
Returns:
|
|
178
202
|
List of file objects with name, id, size, webUrl
|
|
179
203
|
"""
|
|
204
|
+
logger.info(f"Listing documents in '{folder_path}'")
|
|
180
205
|
site_id = self._get_site_id()
|
|
181
206
|
drive_id = self._get_drive_id()
|
|
182
207
|
|
|
@@ -186,24 +211,30 @@ class GraphAPIClient:
|
|
|
186
211
|
else:
|
|
187
212
|
url = f"{self.graph_endpoint}/sites/{site_id}/drives/{drive_id}/root/children"
|
|
188
213
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
214
|
+
logger.info(f"Fetching documents from: {url}")
|
|
215
|
+
try:
|
|
216
|
+
response = requests.get(url, headers=self._get_headers(), timeout=30)
|
|
217
|
+
logger.debug(f"Response status: {response.status_code}")
|
|
218
|
+
self._handle_response(response)
|
|
219
|
+
|
|
220
|
+
items = response.json().get("value", [])
|
|
221
|
+
# Filter to only files
|
|
222
|
+
files = [
|
|
223
|
+
{
|
|
224
|
+
"name": item["name"],
|
|
225
|
+
"id": item["id"],
|
|
226
|
+
"size": item.get("size", 0),
|
|
227
|
+
"webUrl": item.get("webUrl", ""),
|
|
228
|
+
}
|
|
229
|
+
for item in items
|
|
230
|
+
if "file" in item
|
|
231
|
+
]
|
|
232
|
+
|
|
233
|
+
logger.info(f"Found {len(files)} files in '{folder_path}'")
|
|
234
|
+
return files
|
|
235
|
+
except requests.exceptions.RequestException as e:
|
|
236
|
+
logger.error(f"Network error listing documents: {type(e).__name__}: {e}", exc_info=True)
|
|
237
|
+
raise
|
|
207
238
|
|
|
208
239
|
def get_file_content(self, file_path: str) -> bytes:
|
|
209
240
|
"""
|
|
@@ -215,17 +246,24 @@ class GraphAPIClient:
|
|
|
215
246
|
Returns:
|
|
216
247
|
File content as bytes
|
|
217
248
|
"""
|
|
249
|
+
logger.info(f"Getting content for file '{file_path}'")
|
|
218
250
|
site_id = self._get_site_id()
|
|
219
251
|
drive_id = self._get_drive_id()
|
|
220
252
|
|
|
221
253
|
encoded_path = quote(file_path)
|
|
222
254
|
url = f"{self.graph_endpoint}/sites/{site_id}/drives/{drive_id}/root:/{encoded_path}:/content"
|
|
223
255
|
|
|
224
|
-
|
|
225
|
-
|
|
256
|
+
logger.info(f"Fetching file content from: {url}")
|
|
257
|
+
try:
|
|
258
|
+
response = requests.get(url, headers=self._get_headers(), timeout=60)
|
|
259
|
+
logger.debug(f"Response status: {response.status_code}")
|
|
260
|
+
self._handle_response(response)
|
|
226
261
|
|
|
227
|
-
|
|
228
|
-
|
|
262
|
+
logger.info(f"Retrieved content for '{file_path}' ({len(response.content)} bytes)")
|
|
263
|
+
return response.content
|
|
264
|
+
except requests.exceptions.RequestException as e:
|
|
265
|
+
logger.error(f"Network error getting file content: {type(e).__name__}: {e}", exc_info=True)
|
|
266
|
+
raise
|
|
229
267
|
|
|
230
268
|
def upload_file(self, folder_path: str, file_name: str, content: bytes) -> Dict[str, Any]:
|
|
231
269
|
"""
|
|
@@ -239,6 +277,7 @@ class GraphAPIClient:
|
|
|
239
277
|
Returns:
|
|
240
278
|
File metadata
|
|
241
279
|
"""
|
|
280
|
+
logger.info(f"Uploading file '{file_name}' to '{folder_path}' ({len(content)} bytes)")
|
|
242
281
|
site_id = self._get_site_id()
|
|
243
282
|
drive_id = self._get_drive_id()
|
|
244
283
|
|
|
@@ -250,14 +289,20 @@ class GraphAPIClient:
|
|
|
250
289
|
encoded_path = quote(full_path)
|
|
251
290
|
url = f"{self.graph_endpoint}/sites/{site_id}/drives/{drive_id}/root:/{encoded_path}:/content"
|
|
252
291
|
|
|
292
|
+
logger.info(f"Uploading to: {url}")
|
|
253
293
|
headers = self._get_headers()
|
|
254
294
|
headers["Content-Type"] = "application/octet-stream"
|
|
255
295
|
|
|
256
|
-
|
|
257
|
-
|
|
296
|
+
try:
|
|
297
|
+
response = requests.put(url, headers=headers, data=content, timeout=120)
|
|
298
|
+
logger.debug(f"Response status: {response.status_code}")
|
|
299
|
+
self._handle_response(response)
|
|
258
300
|
|
|
259
|
-
|
|
260
|
-
|
|
301
|
+
logger.info(f"Successfully uploaded '{file_name}' to '{folder_path}'")
|
|
302
|
+
return response.json()
|
|
303
|
+
except requests.exceptions.RequestException as e:
|
|
304
|
+
logger.error(f"Network error uploading file: {type(e).__name__}: {e}", exc_info=True)
|
|
305
|
+
raise
|
|
261
306
|
|
|
262
307
|
def delete_file(self, file_path: str) -> None:
|
|
263
308
|
"""
|
|
@@ -266,16 +311,23 @@ class GraphAPIClient:
|
|
|
266
311
|
Args:
|
|
267
312
|
file_path: Relative path to the file
|
|
268
313
|
"""
|
|
314
|
+
logger.info(f"Deleting file '{file_path}'")
|
|
269
315
|
site_id = self._get_site_id()
|
|
270
316
|
drive_id = self._get_drive_id()
|
|
271
317
|
|
|
272
318
|
encoded_path = quote(file_path)
|
|
273
319
|
url = f"{self.graph_endpoint}/sites/{site_id}/drives/{drive_id}/root:/{encoded_path}"
|
|
274
320
|
|
|
275
|
-
|
|
276
|
-
|
|
321
|
+
logger.info(f"Deleting from: {url}")
|
|
322
|
+
try:
|
|
323
|
+
response = requests.delete(url, headers=self._get_headers(), timeout=30)
|
|
324
|
+
logger.debug(f"Response status: {response.status_code}")
|
|
325
|
+
self._handle_response(response)
|
|
277
326
|
|
|
278
|
-
|
|
327
|
+
logger.info(f"Successfully deleted '{file_path}'")
|
|
328
|
+
except requests.exceptions.RequestException as e:
|
|
329
|
+
logger.error(f"Network error deleting file: {type(e).__name__}: {e}", exc_info=True)
|
|
330
|
+
raise
|
|
279
331
|
|
|
280
332
|
def create_folder(self, parent_path: str, folder_name: str) -> Dict[str, Any]:
|
|
281
333
|
"""
|
|
@@ -288,6 +340,7 @@ class GraphAPIClient:
|
|
|
288
340
|
Returns:
|
|
289
341
|
Folder metadata
|
|
290
342
|
"""
|
|
343
|
+
logger.info(f"Creating folder '{folder_name}' in '{parent_path}'")
|
|
291
344
|
site_id = self._get_site_id()
|
|
292
345
|
drive_id = self._get_drive_id()
|
|
293
346
|
|
|
@@ -297,17 +350,23 @@ class GraphAPIClient:
|
|
|
297
350
|
else:
|
|
298
351
|
url = f"{self.graph_endpoint}/sites/{site_id}/drives/{drive_id}/root/children"
|
|
299
352
|
|
|
353
|
+
logger.info(f"Creating folder at: {url}")
|
|
300
354
|
payload = {
|
|
301
355
|
"name": folder_name,
|
|
302
356
|
"folder": {},
|
|
303
357
|
"@microsoft.graph.conflictBehavior": "fail"
|
|
304
358
|
}
|
|
305
359
|
|
|
306
|
-
|
|
307
|
-
|
|
360
|
+
try:
|
|
361
|
+
response = requests.post(url, headers=self._get_headers(), json=payload, timeout=30)
|
|
362
|
+
logger.debug(f"Response status: {response.status_code}")
|
|
363
|
+
self._handle_response(response)
|
|
308
364
|
|
|
309
|
-
|
|
310
|
-
|
|
365
|
+
logger.info(f"Successfully created folder '{folder_name}' in '{parent_path}'")
|
|
366
|
+
return response.json()
|
|
367
|
+
except requests.exceptions.RequestException as e:
|
|
368
|
+
logger.error(f"Network error creating folder: {type(e).__name__}: {e}", exc_info=True)
|
|
369
|
+
raise
|
|
311
370
|
|
|
312
371
|
def delete_folder(self, folder_path: str) -> None:
|
|
313
372
|
"""
|
|
@@ -316,13 +375,20 @@ class GraphAPIClient:
|
|
|
316
375
|
Args:
|
|
317
376
|
folder_path: Relative path to the folder
|
|
318
377
|
"""
|
|
378
|
+
logger.info(f"Deleting folder '{folder_path}'")
|
|
319
379
|
site_id = self._get_site_id()
|
|
320
380
|
drive_id = self._get_drive_id()
|
|
321
381
|
|
|
322
382
|
encoded_path = quote(folder_path)
|
|
323
383
|
url = f"{self.graph_endpoint}/sites/{site_id}/drives/{drive_id}/root:/{encoded_path}"
|
|
324
384
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
385
|
+
logger.info(f"Deleting folder from: {url}")
|
|
386
|
+
try:
|
|
387
|
+
response = requests.delete(url, headers=self._get_headers(), timeout=30)
|
|
388
|
+
logger.debug(f"Response status: {response.status_code}")
|
|
389
|
+
self._handle_response(response)
|
|
390
|
+
|
|
391
|
+
logger.info(f"Successfully deleted folder '{folder_path}'")
|
|
392
|
+
except requests.exceptions.RequestException as e:
|
|
393
|
+
logger.error(f"Network error deleting folder: {type(e).__name__}: {e}", exc_info=True)
|
|
394
|
+
raise
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
mcp_sharepoint/__init__.py,sha256=sSJtlX91mBQ4fM12R8XK7Vrkkr3YPJqriE8LZP157vM,20969
|
|
2
|
+
mcp_sharepoint/__main__.py,sha256=4iVDdDZx4rQ4Zo-x0RaCrT-NKeGObIz_ks3YF8di2nA,132
|
|
3
|
+
mcp_sharepoint/auth.py,sha256=fwOCsg1pv0cN26hNlsHhJhGckeDkJCiXZrMmiBn9jf4,18156
|
|
4
|
+
mcp_sharepoint/graph_api.py,sha256=GNZXKTyTKqSL9t4AAyUSszmhulyJ5cX4e5tlxpYUuYM,14510
|
|
5
|
+
mcp_sharepoint_us-2.0.14.dist-info/licenses/LICENSE,sha256=SRM8juGH4GjIqnl5rrp-P-S5mW5h2mINOPx5-wOZG6s,1112
|
|
6
|
+
mcp_sharepoint_us-2.0.14.dist-info/METADATA,sha256=reWorSnGr5fY68_KV1OK4kTj2Me8aYBRzM-lgt6Jykw,11402
|
|
7
|
+
mcp_sharepoint_us-2.0.14.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
8
|
+
mcp_sharepoint_us-2.0.14.dist-info/entry_points.txt,sha256=UZOa_7OLI41rmsErbvnSz9RahPMGQVcqZUFMphOcjbY,57
|
|
9
|
+
mcp_sharepoint_us-2.0.14.dist-info/top_level.txt,sha256=R6mRoWe61lz4kUSKGV6S2XVbE7825xfC_J-ouZIYpuo,15
|
|
10
|
+
mcp_sharepoint_us-2.0.14.dist-info/RECORD,,
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
mcp_sharepoint/__init__.py,sha256=318-XBsPcTt2EH_B9j-ym_GFs91_0Kb-0WiLKfEV-L0,19744
|
|
2
|
-
mcp_sharepoint/__main__.py,sha256=4iVDdDZx4rQ4Zo-x0RaCrT-NKeGObIz_ks3YF8di2nA,132
|
|
3
|
-
mcp_sharepoint/auth.py,sha256=03p8ylIkrlNoVuVSJ96nnqUd8n7QnwWXWXkkV7y01AU,17598
|
|
4
|
-
mcp_sharepoint/graph_api.py,sha256=y3Q5OHkitAsp7QN1PFIf_sh7g5DShLEfWUlHzIHeS24,10571
|
|
5
|
-
mcp_sharepoint_us-2.0.13.dist-info/licenses/LICENSE,sha256=SRM8juGH4GjIqnl5rrp-P-S5mW5h2mINOPx5-wOZG6s,1112
|
|
6
|
-
mcp_sharepoint_us-2.0.13.dist-info/METADATA,sha256=YNzf3j597kLjomxk9NDhQSHVTc41XGWx-vzVnnFWQHg,11402
|
|
7
|
-
mcp_sharepoint_us-2.0.13.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
8
|
-
mcp_sharepoint_us-2.0.13.dist-info/entry_points.txt,sha256=UZOa_7OLI41rmsErbvnSz9RahPMGQVcqZUFMphOcjbY,57
|
|
9
|
-
mcp_sharepoint_us-2.0.13.dist-info/top_level.txt,sha256=R6mRoWe61lz4kUSKGV6S2XVbE7825xfC_J-ouZIYpuo,15
|
|
10
|
-
mcp_sharepoint_us-2.0.13.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|