mcp-sharepoint-us 2.0.15__tar.gz → 2.0.17__tar.gz
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_us-2.0.15/src/mcp_sharepoint_us.egg-info → mcp_sharepoint_us-2.0.17}/PKG-INFO +1 -1
- {mcp_sharepoint_us-2.0.15 → mcp_sharepoint_us-2.0.17}/pyproject.toml +1 -1
- {mcp_sharepoint_us-2.0.15 → mcp_sharepoint_us-2.0.17}/src/mcp_sharepoint/__init__.py +4 -1
- {mcp_sharepoint_us-2.0.15 → mcp_sharepoint_us-2.0.17}/src/mcp_sharepoint/graph_api.py +73 -16
- {mcp_sharepoint_us-2.0.15 → mcp_sharepoint_us-2.0.17/src/mcp_sharepoint_us.egg-info}/PKG-INFO +1 -1
- {mcp_sharepoint_us-2.0.15 → mcp_sharepoint_us-2.0.17}/LICENSE +0 -0
- {mcp_sharepoint_us-2.0.15 → mcp_sharepoint_us-2.0.17}/README.md +0 -0
- {mcp_sharepoint_us-2.0.15 → mcp_sharepoint_us-2.0.17}/setup.cfg +0 -0
- {mcp_sharepoint_us-2.0.15 → mcp_sharepoint_us-2.0.17}/src/mcp_sharepoint/__main__.py +0 -0
- {mcp_sharepoint_us-2.0.15 → mcp_sharepoint_us-2.0.17}/src/mcp_sharepoint/auth.py +0 -0
- {mcp_sharepoint_us-2.0.15 → mcp_sharepoint_us-2.0.17}/src/mcp_sharepoint_us.egg-info/SOURCES.txt +0 -0
- {mcp_sharepoint_us-2.0.15 → mcp_sharepoint_us-2.0.17}/src/mcp_sharepoint_us.egg-info/dependency_links.txt +0 -0
- {mcp_sharepoint_us-2.0.15 → mcp_sharepoint_us-2.0.17}/src/mcp_sharepoint_us.egg-info/entry_points.txt +0 -0
- {mcp_sharepoint_us-2.0.15 → mcp_sharepoint_us-2.0.17}/src/mcp_sharepoint_us.egg-info/requires.txt +0 -0
- {mcp_sharepoint_us-2.0.15 → mcp_sharepoint_us-2.0.17}/src/mcp_sharepoint_us.egg-info/top_level.txt +0 -0
|
@@ -43,11 +43,13 @@ def ensure_context(func):
|
|
|
43
43
|
client_id = os.getenv("SHP_ID_APP")
|
|
44
44
|
client_secret = os.getenv("SHP_ID_APP_SECRET")
|
|
45
45
|
tenant_id = os.getenv("SHP_TENANT_ID")
|
|
46
|
+
document_library = os.getenv("SHP_DOC_LIBRARY", "Shared Documents")
|
|
46
47
|
cloud = "government" if ".sharepoint.us" in site_url else "commercial"
|
|
47
48
|
|
|
48
49
|
logger.info(f"Site URL: {site_url}")
|
|
49
50
|
logger.info(f"Tenant ID: {tenant_id}")
|
|
50
51
|
logger.info(f"Client ID: {client_id}")
|
|
52
|
+
logger.info(f"Document Library: {document_library}")
|
|
51
53
|
logger.info(f"Cloud: {cloud}")
|
|
52
54
|
|
|
53
55
|
# Create shared authenticator
|
|
@@ -70,7 +72,8 @@ def ensure_context(func):
|
|
|
70
72
|
|
|
71
73
|
graph_client = GraphAPIClient(
|
|
72
74
|
site_url=site_url,
|
|
73
|
-
token_callback=get_token
|
|
75
|
+
token_callback=get_token,
|
|
76
|
+
document_library_name=document_library
|
|
74
77
|
)
|
|
75
78
|
logger.info("Graph API client initialized successfully")
|
|
76
79
|
|
|
@@ -23,16 +23,18 @@ class GraphAPIClient:
|
|
|
23
23
|
where SharePoint REST API may not support app-only authentication.
|
|
24
24
|
"""
|
|
25
25
|
|
|
26
|
-
def __init__(self, site_url: str, token_callback):
|
|
26
|
+
def __init__(self, site_url: str, token_callback, document_library_name: str = "Documents"):
|
|
27
27
|
"""
|
|
28
28
|
Initialize Graph API client.
|
|
29
29
|
|
|
30
30
|
Args:
|
|
31
31
|
site_url: SharePoint site URL (e.g., https://tenant.sharepoint.us/sites/SiteName)
|
|
32
32
|
token_callback: Function that returns access token
|
|
33
|
+
document_library_name: Name of the document library to use (e.g., "Shared Documents1")
|
|
33
34
|
"""
|
|
34
35
|
self.site_url = site_url.rstrip("/")
|
|
35
36
|
self.token_callback = token_callback
|
|
37
|
+
self.document_library_name = document_library_name
|
|
36
38
|
self._site_id = None
|
|
37
39
|
self._drive_id = None # Cache drive ID to avoid repeated API calls
|
|
38
40
|
|
|
@@ -105,7 +107,8 @@ class GraphAPIClient:
|
|
|
105
107
|
|
|
106
108
|
# 3. SSL/TLS Test (if HTTPS)
|
|
107
109
|
if parsed.scheme == "https":
|
|
108
|
-
logger.info(f"[TLS] Testing TLS handshake...")
|
|
110
|
+
logger.info(f"[TLS] Testing TLS handshake to {hostname}...")
|
|
111
|
+
logger.info(f"[TLS] This will attempt to establish encrypted HTTPS connection")
|
|
109
112
|
context = ssl.create_default_context()
|
|
110
113
|
try:
|
|
111
114
|
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
|
|
@@ -122,7 +125,22 @@ class GraphAPIClient:
|
|
|
122
125
|
logger.info(f"[TLS] Certificate subject: {subject.get('commonName', 'N/A')}")
|
|
123
126
|
logger.info(f"[TLS] Certificate issuer: {dict(x[0] for x in cert['issuer']).get('organizationName', 'N/A')}")
|
|
124
127
|
except ssl.SSLError as e:
|
|
125
|
-
logger.error(f"[TLS] ✗ TLS handshake failed: {e}")
|
|
128
|
+
logger.error(f"[TLS] ✗ TLS/SSL handshake failed: {e}")
|
|
129
|
+
logger.error(f"[TLS] This could indicate:")
|
|
130
|
+
logger.error(f"[TLS] - Certificate validation failure")
|
|
131
|
+
logger.error(f"[TLS] - TLS version mismatch")
|
|
132
|
+
logger.error(f"[TLS] - Cipher suite incompatibility")
|
|
133
|
+
return
|
|
134
|
+
except ConnectionResetError as e:
|
|
135
|
+
logger.error(f"[TLS] ✗ Connection reset during TLS handshake")
|
|
136
|
+
logger.error(f"[TLS] TCP connection was established BUT connection dropped during TLS negotiation")
|
|
137
|
+
logger.error(f"[TLS] This indicates:")
|
|
138
|
+
logger.error(f"[TLS] - Firewall is doing deep packet inspection (DPI)")
|
|
139
|
+
logger.error(f"[TLS] - Firewall is blocking TLS connections to {hostname}")
|
|
140
|
+
logger.error(f"[TLS] - SNI (Server Name Indication) filtering is active")
|
|
141
|
+
logger.error(f"[TLS]")
|
|
142
|
+
logger.error(f"[TLS] SOLUTION: Ask network team to whitelist {hostname} in firewall")
|
|
143
|
+
logger.error(f"[TLS] The firewall needs to allow TLS/HTTPS traffic to this endpoint")
|
|
126
144
|
return
|
|
127
145
|
except socket.timeout:
|
|
128
146
|
logger.error(f"[TCP] ✗ Connection timeout after 10 seconds")
|
|
@@ -243,16 +261,32 @@ class GraphAPIClient:
|
|
|
243
261
|
except requests.exceptions.ConnectionError as e:
|
|
244
262
|
logger.error(f"✗ ConnectionError getting site ID: {e}", exc_info=True)
|
|
245
263
|
logger.error("This indicates the connection was established but then dropped.")
|
|
246
|
-
logger.error("Running comprehensive diagnostics...")
|
|
264
|
+
logger.error("Running comprehensive diagnostics to identify the exact failure point...")
|
|
265
|
+
logger.error("")
|
|
247
266
|
|
|
248
267
|
# Run diagnostics to help identify the issue
|
|
249
268
|
self._diagnose_connectivity(url)
|
|
250
269
|
|
|
251
|
-
logger.error("
|
|
252
|
-
logger.error("
|
|
270
|
+
logger.error("")
|
|
271
|
+
logger.error("=" * 70)
|
|
272
|
+
logger.error("DIAGNOSIS COMPLETE")
|
|
273
|
+
logger.error("=" * 70)
|
|
274
|
+
logger.error("")
|
|
275
|
+
logger.error("Most common causes of 'Connection reset by peer':")
|
|
276
|
+
logger.error("")
|
|
277
|
+
logger.error("1. ⚠️ FIREWALL BLOCKING HTTPS/TLS (Most likely based on symptoms)")
|
|
278
|
+
logger.error(" - TCP connection succeeds")
|
|
279
|
+
logger.error(" - Connection drops during TLS handshake")
|
|
280
|
+
logger.error(" - Indicates deep packet inspection (DPI) is active")
|
|
281
|
+
logger.error(" - Solution: Ask network team to whitelist graph.microsoft.us")
|
|
282
|
+
logger.error("")
|
|
253
283
|
logger.error("2. Proxy configuration needed")
|
|
254
|
-
logger.error("
|
|
255
|
-
logger.error("
|
|
284
|
+
logger.error(" - Set HTTP_PROXY and HTTPS_PROXY environment variables")
|
|
285
|
+
logger.error("")
|
|
286
|
+
logger.error("3. SSL/TLS version or certificate issue")
|
|
287
|
+
logger.error(" - Less likely if TCP connects successfully")
|
|
288
|
+
logger.error("")
|
|
289
|
+
logger.error("=" * 70)
|
|
256
290
|
raise
|
|
257
291
|
|
|
258
292
|
except requests.exceptions.Timeout:
|
|
@@ -265,7 +299,8 @@ class GraphAPIClient:
|
|
|
265
299
|
|
|
266
300
|
def _get_drive_id(self) -> str:
|
|
267
301
|
"""
|
|
268
|
-
Get the
|
|
302
|
+
Get the document library drive ID by name.
|
|
303
|
+
Fetches all drives and finds the one matching self.document_library_name.
|
|
269
304
|
Caches the result for reuse.
|
|
270
305
|
"""
|
|
271
306
|
if self._drive_id:
|
|
@@ -273,9 +308,10 @@ class GraphAPIClient:
|
|
|
273
308
|
return self._drive_id
|
|
274
309
|
|
|
275
310
|
site_id = self._get_site_id()
|
|
276
|
-
url = f"{self.graph_endpoint}/sites/{site_id}/
|
|
311
|
+
url = f"{self.graph_endpoint}/sites/{site_id}/drives"
|
|
277
312
|
|
|
278
|
-
logger.info(f"Fetching
|
|
313
|
+
logger.info(f"Fetching all drives from: {url}")
|
|
314
|
+
logger.info(f"Looking for document library named: '{self.document_library_name}'")
|
|
279
315
|
|
|
280
316
|
try:
|
|
281
317
|
logger.debug(f"Sending GET request to: {url}")
|
|
@@ -284,16 +320,37 @@ class GraphAPIClient:
|
|
|
284
320
|
logger.debug(f"Response received - Status: {response.status_code}")
|
|
285
321
|
self._handle_response(response)
|
|
286
322
|
|
|
287
|
-
|
|
288
|
-
logger.info(f"
|
|
289
|
-
|
|
323
|
+
drives = response.json().get("value", [])
|
|
324
|
+
logger.info(f"Found {len(drives)} drives in site")
|
|
325
|
+
|
|
326
|
+
# Log all available drives for debugging
|
|
327
|
+
for drive in drives:
|
|
328
|
+
drive_name = drive.get("name", "Unknown")
|
|
329
|
+
drive_id = drive.get("id", "Unknown")
|
|
330
|
+
logger.debug(f" Drive: '{drive_name}' (ID: {drive_id})")
|
|
331
|
+
|
|
332
|
+
# Find the drive matching the configured document library name
|
|
333
|
+
for drive in drives:
|
|
334
|
+
if drive.get("name") == self.document_library_name:
|
|
335
|
+
self._drive_id = drive["id"]
|
|
336
|
+
logger.info(f"✓ Found matching drive '{self.document_library_name}' with ID: {self._drive_id}")
|
|
337
|
+
return self._drive_id
|
|
338
|
+
|
|
339
|
+
# If no match found, raise an error with helpful message
|
|
340
|
+
available_drives = [d.get("name", "Unknown") for d in drives]
|
|
341
|
+
error_msg = (
|
|
342
|
+
f"Could not find document library named '{self.document_library_name}'. "
|
|
343
|
+
f"Available drives: {', '.join(available_drives)}"
|
|
344
|
+
)
|
|
345
|
+
logger.error(error_msg)
|
|
346
|
+
raise ValueError(error_msg)
|
|
290
347
|
|
|
291
348
|
except requests.exceptions.ConnectionError as e:
|
|
292
|
-
logger.error(f"✗ ConnectionError getting
|
|
349
|
+
logger.error(f"✗ ConnectionError getting drives: {e}", exc_info=True)
|
|
293
350
|
raise
|
|
294
351
|
|
|
295
352
|
except requests.exceptions.RequestException as e:
|
|
296
|
-
logger.error(f"✗ Network error getting
|
|
353
|
+
logger.error(f"✗ Network error getting drives: {type(e).__name__}: {e}", exc_info=True)
|
|
297
354
|
raise
|
|
298
355
|
|
|
299
356
|
def list_folders(self, folder_path: str = "") -> List[Dict[str, Any]]:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_sharepoint_us-2.0.15 → mcp_sharepoint_us-2.0.17}/src/mcp_sharepoint_us.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_sharepoint_us-2.0.15 → mcp_sharepoint_us-2.0.17}/src/mcp_sharepoint_us.egg-info/requires.txt
RENAMED
|
File without changes
|
{mcp_sharepoint_us-2.0.15 → mcp_sharepoint_us-2.0.17}/src/mcp_sharepoint_us.egg-info/top_level.txt
RENAMED
|
File without changes
|