@mcp-consultant-tools/sharepoint 21.0.0-beta.2 → 21.0.0-beta.4
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.
- package/build/SharePointService.d.ts +9 -0
- package/build/SharePointService.d.ts.map +1 -1
- package/build/SharePointService.js +13 -0
- package/build/SharePointService.js.map +1 -1
- package/build/http-server.d.ts +3 -0
- package/build/http-server.d.ts.map +1 -0
- package/build/http-server.js +216 -0
- package/build/http-server.js.map +1 -0
- package/build/index.d.ts +6 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +42 -1036
- package/build/index.js.map +1 -1
- package/build/services/FileOperationsService.d.ts +52 -0
- package/build/services/FileOperationsService.d.ts.map +1 -0
- package/build/services/FileOperationsService.js +451 -0
- package/build/services/FileOperationsService.js.map +1 -0
- package/build/tools/prompts.d.ts +11 -0
- package/build/tools/prompts.d.ts.map +1 -0
- package/build/tools/prompts.js +470 -0
- package/build/tools/prompts.js.map +1 -0
- package/build/tools/read-tools.d.ts +13 -0
- package/build/tools/read-tools.d.ts.map +1 -0
- package/build/tools/read-tools.js +271 -0
- package/build/tools/read-tools.js.map +1 -0
- package/build/tools/write-tools.d.ts +14 -0
- package/build/tools/write-tools.d.ts.map +1 -0
- package/build/tools/write-tools.js +137 -0
- package/build/tools/write-tools.js.map +1 -0
- package/build/types/sharepoint-types.d.ts +34 -0
- package/build/types/sharepoint-types.d.ts.map +1 -1
- package/build/utils/tool-examples.d.ts +35 -0
- package/build/utils/tool-examples.d.ts.map +1 -0
- package/build/utils/tool-examples.js +55 -0
- package/build/utils/tool-examples.js.map +1 -0
- package/package.json +5 -2
package/build/index.js
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* SharePoint MCP Server - Orchestrator
|
|
4
|
+
*
|
|
5
|
+
* Slim entry point that delegates to extracted tool/prompt modules.
|
|
6
|
+
* File management tools require feature flags (SHAREPOINT_ENABLE_WRITE, SHAREPOINT_ENABLE_DELETE).
|
|
7
|
+
*/
|
|
2
8
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
9
|
import { pathToFileURL } from "node:url";
|
|
4
10
|
import { realpathSync } from "node:fs";
|
|
5
11
|
import { createMcpServer, createEnvLoader } from "@mcp-consultant-tools/core";
|
|
6
12
|
import { SharePointService } from "./SharePointService.js";
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
13
|
+
import { FileOperationsService } from "./services/FileOperationsService.js";
|
|
14
|
+
import { registerSharePointPrompts } from "./tools/prompts.js";
|
|
15
|
+
import { registerReadTools } from "./tools/read-tools.js";
|
|
16
|
+
import { registerWriteTools } from "./tools/write-tools.js";
|
|
9
17
|
export function registerSharePointTools(server, sharepointService) {
|
|
10
18
|
let service = sharepointService || null;
|
|
19
|
+
let fileOps = null;
|
|
11
20
|
function getSharePointService() {
|
|
12
21
|
if (!service) {
|
|
13
22
|
const missingConfig = [];
|
|
@@ -52,1047 +61,44 @@ export function registerSharePointTools(server, sharepointService) {
|
|
|
52
61
|
}
|
|
53
62
|
return service;
|
|
54
63
|
}
|
|
64
|
+
function getFileOperationsService() {
|
|
65
|
+
if (!fileOps) {
|
|
66
|
+
const spoService = getSharePointService();
|
|
67
|
+
fileOps = new FileOperationsService(spoService, {
|
|
68
|
+
maxDownloadSizeMB: parseInt(process.env.SHAREPOINT_MAX_DOWNLOAD_SIZE_MB || '50', 10),
|
|
69
|
+
maxUploadSizeMB: parseInt(process.env.SHAREPOINT_MAX_UPLOAD_SIZE_MB || '100', 10),
|
|
70
|
+
});
|
|
71
|
+
console.error("FileOperationsService initialized");
|
|
72
|
+
}
|
|
73
|
+
return fileOps;
|
|
74
|
+
}
|
|
55
75
|
// PowerPlatform integration (requires cross-package dependency)
|
|
56
76
|
function getPowerPlatformService() {
|
|
57
77
|
throw new Error('PowerPlatform integration not available in standalone SharePoint package. ' +
|
|
58
78
|
'Use the complete @mcp-consultant-tools package for cross-service validation.');
|
|
59
79
|
}
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
siteId: z.string().describe("Site ID from configuration"),
|
|
65
|
-
}, async ({ siteId }) => {
|
|
66
|
-
try {
|
|
67
|
-
const service = getSharePointService();
|
|
68
|
-
// Get site info
|
|
69
|
-
const site = await service.getSiteInfo(siteId);
|
|
70
|
-
// Get drives (document libraries)
|
|
71
|
-
const drives = await service.listDrives(siteId);
|
|
72
|
-
// Build report
|
|
73
|
-
const sections = [];
|
|
74
|
-
sections.push(spoFormatters.formatSiteOverviewAsMarkdown(site));
|
|
75
|
-
sections.push('');
|
|
76
|
-
sections.push('## Document Libraries');
|
|
77
|
-
sections.push(spoFormatters.formatDrivesAsMarkdown(drives));
|
|
78
|
-
return {
|
|
79
|
-
description: `SharePoint site overview: ${site.displayName}`,
|
|
80
|
-
messages: [
|
|
81
|
-
{
|
|
82
|
-
role: "user",
|
|
83
|
-
content: {
|
|
84
|
-
type: "text",
|
|
85
|
-
text: `Show overview of SharePoint site ${siteId}`,
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
role: "assistant",
|
|
90
|
-
content: {
|
|
91
|
-
type: "text",
|
|
92
|
-
text: sections.join('\n'),
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
],
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
catch (error) {
|
|
99
|
-
console.error("Error generating site overview:", error);
|
|
100
|
-
throw error;
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
server.prompt("spo-library-details", {
|
|
104
|
-
siteId: z.string().describe("Site ID"),
|
|
105
|
-
driveId: z.string().describe("Drive (library) ID"),
|
|
106
|
-
}, async ({ siteId, driveId }) => {
|
|
107
|
-
try {
|
|
108
|
-
const service = getSharePointService();
|
|
109
|
-
// Get drive info
|
|
110
|
-
const drive = await service.getDriveInfo(siteId, driveId);
|
|
111
|
-
// Get recent items
|
|
112
|
-
const recentItems = await service.getRecentItems(siteId, driveId, 10, 30);
|
|
113
|
-
// Build report
|
|
114
|
-
const sections = [];
|
|
115
|
-
sections.push(spoFormatters.formatDriveDetailsAsMarkdown(drive));
|
|
116
|
-
sections.push('');
|
|
117
|
-
sections.push('## Recent Activity (Last 30 days)');
|
|
118
|
-
sections.push(spoFormatters.formatItemsAsMarkdown(recentItems));
|
|
119
|
-
return {
|
|
120
|
-
description: `Document library details: ${drive.name}`,
|
|
121
|
-
messages: [
|
|
122
|
-
{
|
|
123
|
-
role: "user",
|
|
124
|
-
content: {
|
|
125
|
-
type: "text",
|
|
126
|
-
text: `Show details for document library ${driveId} in site ${siteId}`,
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
role: "assistant",
|
|
131
|
-
content: {
|
|
132
|
-
type: "text",
|
|
133
|
-
text: sections.join('\n'),
|
|
134
|
-
},
|
|
135
|
-
},
|
|
136
|
-
],
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
catch (error) {
|
|
140
|
-
console.error("Error generating library details:", error);
|
|
141
|
-
throw error;
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
server.prompt("spo-document-search", {
|
|
145
|
-
siteId: z.string().describe("Site ID"),
|
|
146
|
-
driveId: z.string().describe("Drive ID"),
|
|
147
|
-
query: z.string().describe("Search query (filename or keywords)"),
|
|
148
|
-
}, async ({ siteId, driveId, query }) => {
|
|
149
|
-
try {
|
|
150
|
-
const service = getSharePointService();
|
|
151
|
-
// Search items
|
|
152
|
-
const searchResults = await service.searchItems(siteId, driveId, query);
|
|
153
|
-
// Build report
|
|
154
|
-
const sections = [];
|
|
155
|
-
sections.push(`# 🔍 Search Results: "${query}"`);
|
|
156
|
-
sections.push('');
|
|
157
|
-
sections.push(`Found ${searchResults.items.length} result(s)`);
|
|
158
|
-
sections.push('');
|
|
159
|
-
sections.push(spoFormatters.formatItemsAsMarkdown(searchResults.items));
|
|
160
|
-
return {
|
|
161
|
-
description: `Search results for "${query}"`,
|
|
162
|
-
messages: [
|
|
163
|
-
{
|
|
164
|
-
role: "user",
|
|
165
|
-
content: {
|
|
166
|
-
type: "text",
|
|
167
|
-
text: `Search for "${query}" in drive ${driveId} of site ${siteId}`,
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
|
-
{
|
|
171
|
-
role: "assistant",
|
|
172
|
-
content: {
|
|
173
|
-
type: "text",
|
|
174
|
-
text: sections.join('\n'),
|
|
175
|
-
},
|
|
176
|
-
},
|
|
177
|
-
],
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
catch (error) {
|
|
181
|
-
console.error("Error generating search results:", error);
|
|
182
|
-
throw error;
|
|
183
|
-
}
|
|
184
|
-
});
|
|
185
|
-
server.prompt("spo-recent-activity", {
|
|
186
|
-
siteId: z.string().describe("Site ID"),
|
|
187
|
-
driveId: z.string().describe("Drive ID"),
|
|
188
|
-
days: z.string().optional().describe("Number of days to look back (default: 7)"),
|
|
189
|
-
}, async ({ siteId, driveId, days }) => {
|
|
190
|
-
try {
|
|
191
|
-
const service = getSharePointService();
|
|
192
|
-
const daysBack = days ? parseInt(days) : 7;
|
|
193
|
-
const recentItems = await service.getRecentItems(siteId, driveId, 50, daysBack);
|
|
194
|
-
// Build report
|
|
195
|
-
const sections = [];
|
|
196
|
-
sections.push(`# 📅 Recent Activity (Last ${daysBack} days)`);
|
|
197
|
-
sections.push('');
|
|
198
|
-
sections.push(`**Document Library:** ${driveId}`);
|
|
199
|
-
sections.push(`**Total Changes:** ${recentItems.length}`);
|
|
200
|
-
sections.push('');
|
|
201
|
-
sections.push(spoFormatters.formatItemsAsMarkdown(recentItems));
|
|
202
|
-
return {
|
|
203
|
-
description: `Recent activity for last ${daysBack} days`,
|
|
204
|
-
messages: [
|
|
205
|
-
{
|
|
206
|
-
role: "user",
|
|
207
|
-
content: {
|
|
208
|
-
type: "text",
|
|
209
|
-
text: `Show recent activity in drive ${driveId} for last ${daysBack} days`,
|
|
210
|
-
},
|
|
211
|
-
},
|
|
212
|
-
{
|
|
213
|
-
role: "assistant",
|
|
214
|
-
content: {
|
|
215
|
-
type: "text",
|
|
216
|
-
text: sections.join('\n'),
|
|
217
|
-
},
|
|
218
|
-
},
|
|
219
|
-
],
|
|
220
|
-
};
|
|
80
|
+
// Feature flag guards
|
|
81
|
+
function checkWriteEnabled() {
|
|
82
|
+
if (process.env.SHAREPOINT_ENABLE_WRITE !== 'true') {
|
|
83
|
+
throw new Error('Write operations are disabled. Set SHAREPOINT_ENABLE_WRITE=true to enable.');
|
|
221
84
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
});
|
|
227
|
-
server.prompt("spo-validate-crm-integration", {
|
|
228
|
-
documentLocationId: z.string().describe("Document location ID from PowerPlatform"),
|
|
229
|
-
}, async ({ documentLocationId }) => {
|
|
230
|
-
try {
|
|
231
|
-
const spoService = getSharePointService();
|
|
232
|
-
const ppService = getPowerPlatformService();
|
|
233
|
-
// Validate document location
|
|
234
|
-
const result = await spoService.validateDocumentLocation(ppService, documentLocationId);
|
|
235
|
-
// Generate analysis
|
|
236
|
-
const sections = [];
|
|
237
|
-
sections.push(spoFormatters.formatValidationResultAsMarkdown(result));
|
|
238
|
-
return {
|
|
239
|
-
description: `Validation result for document location ${documentLocationId}`,
|
|
240
|
-
messages: [
|
|
241
|
-
{
|
|
242
|
-
role: "user",
|
|
243
|
-
content: {
|
|
244
|
-
type: "text",
|
|
245
|
-
text: `Validate PowerPlatform document location ${documentLocationId}`,
|
|
246
|
-
},
|
|
247
|
-
},
|
|
248
|
-
{
|
|
249
|
-
role: "assistant",
|
|
250
|
-
content: {
|
|
251
|
-
type: "text",
|
|
252
|
-
text: sections.join('\n'),
|
|
253
|
-
},
|
|
254
|
-
},
|
|
255
|
-
],
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
catch (error) {
|
|
259
|
-
console.error("Error validating CRM integration:", error);
|
|
260
|
-
throw error;
|
|
261
|
-
}
|
|
262
|
-
});
|
|
263
|
-
server.prompt("spo-document-location-audit", {
|
|
264
|
-
entityName: z.string().optional().describe("Entity logical name (e.g., 'account')"),
|
|
265
|
-
recordId: z.string().optional().describe("Record ID (GUID)"),
|
|
266
|
-
}, async ({ entityName, recordId }) => {
|
|
267
|
-
try {
|
|
268
|
-
const spoService = getSharePointService();
|
|
269
|
-
const ppService = getPowerPlatformService();
|
|
270
|
-
// Get document locations
|
|
271
|
-
const locations = await spoService.getCrmDocumentLocations(ppService, entityName, recordId);
|
|
272
|
-
// Analyze
|
|
273
|
-
const analysis = spoFormatters.analyzeCrmDocumentLocations(locations);
|
|
274
|
-
// Build report
|
|
275
|
-
const sections = [];
|
|
276
|
-
sections.push('# 📋 Document Location Audit');
|
|
277
|
-
sections.push('');
|
|
278
|
-
if (entityName) {
|
|
279
|
-
sections.push(`**Entity:** ${entityName}`);
|
|
280
|
-
}
|
|
281
|
-
if (recordId) {
|
|
282
|
-
sections.push(`**Record ID:** ${recordId}`);
|
|
283
|
-
}
|
|
284
|
-
sections.push('');
|
|
285
|
-
sections.push('## Insights');
|
|
286
|
-
analysis.insights.forEach(insight => {
|
|
287
|
-
sections.push(insight);
|
|
288
|
-
});
|
|
289
|
-
sections.push('');
|
|
290
|
-
sections.push('## Document Locations');
|
|
291
|
-
sections.push(spoFormatters.formatCrmDocumentLocationsAsMarkdown(locations));
|
|
292
|
-
if (analysis.recommendations.length > 0) {
|
|
293
|
-
sections.push('');
|
|
294
|
-
sections.push('## Recommendations');
|
|
295
|
-
analysis.recommendations.forEach(rec => {
|
|
296
|
-
sections.push(`- ${rec}`);
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
return {
|
|
300
|
-
description: `Document location audit${entityName ? ` for ${entityName}` : ''}`,
|
|
301
|
-
messages: [
|
|
302
|
-
{
|
|
303
|
-
role: "user",
|
|
304
|
-
content: {
|
|
305
|
-
type: "text",
|
|
306
|
-
text: `Audit document locations${entityName ? ` for entity ${entityName}` : ''}${recordId ? ` record ${recordId}` : ''}`,
|
|
307
|
-
},
|
|
308
|
-
},
|
|
309
|
-
{
|
|
310
|
-
role: "assistant",
|
|
311
|
-
content: {
|
|
312
|
-
type: "text",
|
|
313
|
-
text: sections.join('\n'),
|
|
314
|
-
},
|
|
315
|
-
},
|
|
316
|
-
],
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
catch (error) {
|
|
320
|
-
console.error("Error generating document location audit:", error);
|
|
321
|
-
throw error;
|
|
322
|
-
}
|
|
323
|
-
});
|
|
324
|
-
server.prompt("spo-migration-verification-report", {
|
|
325
|
-
sourceSiteId: z.string().describe("Source site ID"),
|
|
326
|
-
sourcePath: z.string().describe("Source folder path"),
|
|
327
|
-
targetSiteId: z.string().describe("Target site ID"),
|
|
328
|
-
targetPath: z.string().describe("Target folder path"),
|
|
329
|
-
}, async ({ sourceSiteId, sourcePath, targetSiteId, targetPath }) => {
|
|
330
|
-
try {
|
|
331
|
-
const spoService = getSharePointService();
|
|
332
|
-
const ppService = getPowerPlatformService();
|
|
333
|
-
// Verify migration
|
|
334
|
-
const result = await spoService.verifyDocumentMigration(ppService, sourceSiteId, sourcePath, targetSiteId, targetPath);
|
|
335
|
-
// Analyze
|
|
336
|
-
const analysis = spoFormatters.analyzeMigrationVerification(result);
|
|
337
|
-
// Build report
|
|
338
|
-
const sections = [];
|
|
339
|
-
sections.push(spoFormatters.formatMigrationReportAsMarkdown(result));
|
|
340
|
-
sections.push('');
|
|
341
|
-
sections.push('## Analysis');
|
|
342
|
-
analysis.insights.forEach(insight => {
|
|
343
|
-
sections.push(`- ${insight}`);
|
|
344
|
-
});
|
|
345
|
-
sections.push('');
|
|
346
|
-
sections.push('## Recommendations');
|
|
347
|
-
analysis.recommendations.forEach(rec => {
|
|
348
|
-
sections.push(`- ${rec}`);
|
|
349
|
-
});
|
|
350
|
-
return {
|
|
351
|
-
description: `Migration verification: ${result.status} (${result.successRate}% success)`,
|
|
352
|
-
messages: [
|
|
353
|
-
{
|
|
354
|
-
role: "user",
|
|
355
|
-
content: {
|
|
356
|
-
type: "text",
|
|
357
|
-
text: `Verify document migration from ${sourcePath} to ${targetPath}`,
|
|
358
|
-
},
|
|
359
|
-
},
|
|
360
|
-
{
|
|
361
|
-
role: "assistant",
|
|
362
|
-
content: {
|
|
363
|
-
type: "text",
|
|
364
|
-
text: sections.join('\n'),
|
|
365
|
-
},
|
|
366
|
-
},
|
|
367
|
-
],
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
catch (error) {
|
|
371
|
-
console.error("Error generating migration verification report:", error);
|
|
372
|
-
throw error;
|
|
373
|
-
}
|
|
374
|
-
});
|
|
375
|
-
server.prompt("spo-setup-validation-guide", {}, async () => {
|
|
376
|
-
const guide = `# SharePoint Integration Setup Validation Guide
|
|
377
|
-
|
|
378
|
-
## Prerequisites Checklist
|
|
379
|
-
|
|
380
|
-
### 1. Azure AD App Registration
|
|
381
|
-
- ✅ App registered in Azure Active Directory
|
|
382
|
-
- ✅ Client ID and Client Secret generated
|
|
383
|
-
- ✅ Tenant ID noted
|
|
384
|
-
|
|
385
|
-
### 2. API Permissions
|
|
386
|
-
Required Microsoft Graph API permissions (Application permissions):
|
|
387
|
-
- ✅ Sites.Read.All or Sites.ReadWrite.All
|
|
388
|
-
- ✅ Files.Read.All or Files.ReadWrite.All
|
|
389
|
-
- ✅ Admin consent granted
|
|
390
|
-
|
|
391
|
-
### 3. SharePoint Site Access
|
|
392
|
-
- ✅ Service principal added to site(s) as Site Collection Admin
|
|
393
|
-
- ✅ Site URLs accessible and correct
|
|
394
|
-
|
|
395
|
-
### 4. Configuration
|
|
396
|
-
Environment variables configured:
|
|
397
|
-
- ✅ SHAREPOINT_TENANT_ID
|
|
398
|
-
- ✅ SHAREPOINT_CLIENT_ID
|
|
399
|
-
- ✅ SHAREPOINT_CLIENT_SECRET
|
|
400
|
-
- ✅ SHAREPOINT_SITES (JSON array) or SHAREPOINT_SITE_URL
|
|
401
|
-
|
|
402
|
-
## Testing Steps
|
|
403
|
-
|
|
404
|
-
### Step 1: Test Connection
|
|
405
|
-
\`\`\`
|
|
406
|
-
Use tool: spo-test-connection
|
|
407
|
-
Parameters: { siteId: "your-site-id" }
|
|
408
|
-
Expected: Site information returned with no errors
|
|
409
|
-
\`\`\`
|
|
410
|
-
|
|
411
|
-
### Step 2: List Document Libraries
|
|
412
|
-
\`\`\`
|
|
413
|
-
Use tool: spo-list-drives
|
|
414
|
-
Parameters: { siteId: "your-site-id" }
|
|
415
|
-
Expected: List of document libraries with quota info
|
|
416
|
-
\`\`\`
|
|
417
|
-
|
|
418
|
-
### Step 3: List Files
|
|
419
|
-
\`\`\`
|
|
420
|
-
Use tool: spo-list-items
|
|
421
|
-
Parameters: { siteId: "your-site-id", driveId: "library-id" }
|
|
422
|
-
Expected: List of files and folders
|
|
423
|
-
\`\`\`
|
|
424
|
-
|
|
425
|
-
### Step 4: Test PowerPlatform Integration (Optional)
|
|
426
|
-
\`\`\`
|
|
427
|
-
Use tool: spo-get-crm-doc-locs
|
|
428
|
-
Expected: List of document locations from Dataverse
|
|
429
|
-
\`\`\`
|
|
430
|
-
|
|
431
|
-
## Common Issues
|
|
432
|
-
|
|
433
|
-
### Issue: "Access denied" error
|
|
434
|
-
**Solution:**
|
|
435
|
-
1. Verify API permissions are granted
|
|
436
|
-
2. Ensure admin consent is granted
|
|
437
|
-
3. Check service principal is Site Collection Admin
|
|
438
|
-
|
|
439
|
-
### Issue: "Site not found"
|
|
440
|
-
**Solution:**
|
|
441
|
-
1. Verify site URL is correct (use full URL)
|
|
442
|
-
2. Check site exists and is accessible
|
|
443
|
-
3. Ensure site is in SHAREPOINT_SITES configuration
|
|
444
|
-
|
|
445
|
-
### Issue: "Authentication failed"
|
|
446
|
-
**Solution:**
|
|
447
|
-
1. Verify tenant ID, client ID, and client secret
|
|
448
|
-
2. Check client secret hasn't expired
|
|
449
|
-
3. Ensure app registration is active
|
|
450
|
-
|
|
451
|
-
## Next Steps
|
|
452
|
-
|
|
453
|
-
Once setup is validated:
|
|
454
|
-
1. Configure additional sites in SHAREPOINT_SITES
|
|
455
|
-
2. Set up PowerPlatform integration for document location validation
|
|
456
|
-
3. Use validation tools to audit document locations
|
|
457
|
-
4. Set up migration verification workflows
|
|
458
|
-
|
|
459
|
-
For more help, refer to SETUP.md documentation.
|
|
460
|
-
`;
|
|
461
|
-
return {
|
|
462
|
-
description: "SharePoint integration setup validation guide",
|
|
463
|
-
messages: [
|
|
464
|
-
{
|
|
465
|
-
role: "user",
|
|
466
|
-
content: {
|
|
467
|
-
type: "text",
|
|
468
|
-
text: "Show SharePoint integration setup validation guide",
|
|
469
|
-
},
|
|
470
|
-
},
|
|
471
|
-
{
|
|
472
|
-
role: "assistant",
|
|
473
|
-
content: {
|
|
474
|
-
type: "text",
|
|
475
|
-
text: guide,
|
|
476
|
-
},
|
|
477
|
-
},
|
|
478
|
-
],
|
|
479
|
-
};
|
|
480
|
-
});
|
|
481
|
-
server.prompt("spo-troubleshooting-guide", {
|
|
482
|
-
errorType: z.string().optional().describe("Type of error (e.g., 'access-denied', 'site-not-found')"),
|
|
483
|
-
}, async ({ errorType }) => {
|
|
484
|
-
const guide = `# SharePoint Integration Troubleshooting Guide
|
|
485
|
-
|
|
486
|
-
## Common Error Scenarios
|
|
487
|
-
|
|
488
|
-
### 1. Access Denied (403 Forbidden)
|
|
489
|
-
|
|
490
|
-
**Symptoms:**
|
|
491
|
-
- "Access denied" errors when accessing sites or files
|
|
492
|
-
- "Insufficient permissions" messages
|
|
493
|
-
|
|
494
|
-
**Causes:**
|
|
495
|
-
- Missing API permissions
|
|
496
|
-
- Admin consent not granted
|
|
497
|
-
- Service principal not added to site
|
|
498
|
-
|
|
499
|
-
**Solutions:**
|
|
500
|
-
1. Verify Microsoft Graph API permissions:
|
|
501
|
-
- Sites.Read.All (or Sites.ReadWrite.All)
|
|
502
|
-
- Files.Read.All (or Files.ReadWrite.All)
|
|
503
|
-
2. Grant admin consent in Azure AD
|
|
504
|
-
3. Add service principal as Site Collection Admin:
|
|
505
|
-
- Go to site settings → Site permissions
|
|
506
|
-
- Add app with client ID
|
|
507
|
-
- Grant Full Control or Read permissions
|
|
508
|
-
|
|
509
|
-
### 2. Site Not Found (404 Not Found)
|
|
510
|
-
|
|
511
|
-
**Symptoms:**
|
|
512
|
-
- "Site not found" errors
|
|
513
|
-
- "Resource does not exist" messages
|
|
514
|
-
|
|
515
|
-
**Causes:**
|
|
516
|
-
- Incorrect site URL
|
|
517
|
-
- Site not in SHAREPOINT_SITES configuration
|
|
518
|
-
- Site deleted or moved
|
|
519
|
-
|
|
520
|
-
**Solutions:**
|
|
521
|
-
1. Verify site URL format: https://tenant.sharepoint.com/sites/sitename
|
|
522
|
-
2. Check site exists by visiting in browser
|
|
523
|
-
3. Add site to SHAREPOINT_SITES configuration
|
|
524
|
-
4. Ensure site is not archived or deleted
|
|
525
|
-
|
|
526
|
-
### 3. Authentication Failed (401 Unauthorized)
|
|
527
|
-
|
|
528
|
-
**Symptoms:**
|
|
529
|
-
- "Authentication failed" errors
|
|
530
|
-
- "Invalid credentials" messages
|
|
531
|
-
|
|
532
|
-
**Causes:**
|
|
533
|
-
- Incorrect tenant ID, client ID, or client secret
|
|
534
|
-
- Client secret expired
|
|
535
|
-
- App registration disabled
|
|
536
|
-
|
|
537
|
-
**Solutions:**
|
|
538
|
-
1. Verify credentials in environment variables
|
|
539
|
-
2. Check client secret expiration in Azure AD
|
|
540
|
-
3. Generate new client secret if expired
|
|
541
|
-
4. Ensure app registration is active
|
|
542
|
-
|
|
543
|
-
### 4. Token Acquisition Failed
|
|
544
|
-
|
|
545
|
-
**Symptoms:**
|
|
546
|
-
- "Failed to acquire access token" errors
|
|
547
|
-
- MSAL errors
|
|
548
|
-
|
|
549
|
-
**Causes:**
|
|
550
|
-
- Network connectivity issues
|
|
551
|
-
- Firewall blocking Azure AD
|
|
552
|
-
- Incorrect tenant ID
|
|
553
|
-
|
|
554
|
-
**Solutions:**
|
|
555
|
-
1. Verify network connectivity to login.microsoftonline.com
|
|
556
|
-
2. Check firewall rules
|
|
557
|
-
3. Verify tenant ID is correct
|
|
558
|
-
4. Test authentication manually
|
|
559
|
-
|
|
560
|
-
### 5. Folder Not Found
|
|
561
|
-
|
|
562
|
-
**Symptoms:**
|
|
563
|
-
- "Folder not accessible" in validation results
|
|
564
|
-
- "Item not found" errors
|
|
565
|
-
|
|
566
|
-
**Causes:**
|
|
567
|
-
- Incorrect folder path
|
|
568
|
-
- Folder deleted or moved
|
|
569
|
-
- Permissions issue
|
|
570
|
-
|
|
571
|
-
**Solutions:**
|
|
572
|
-
1. Verify folder path format: /LibraryName/Folder1/Folder2
|
|
573
|
-
2. Check folder exists in SharePoint
|
|
574
|
-
3. Ensure service principal has access
|
|
575
|
-
4. Use spo-list-items to browse folder structure
|
|
576
|
-
|
|
577
|
-
### 6. Document Location Validation Fails
|
|
578
|
-
|
|
579
|
-
**Symptoms:**
|
|
580
|
-
- Validation status: "error" or "warning"
|
|
581
|
-
- Missing or inaccessible folders
|
|
582
|
-
|
|
583
|
-
**Causes:**
|
|
584
|
-
- CRM absolute URL incorrect
|
|
585
|
-
- Site not configured
|
|
586
|
-
- Folder path mismatch
|
|
587
|
-
|
|
588
|
-
**Solutions:**
|
|
589
|
-
1. Verify absolute URL in PowerPlatform
|
|
590
|
-
2. Add site to SHAREPOINT_SITES configuration
|
|
591
|
-
3. Check folder path matches SharePoint structure
|
|
592
|
-
4. Use spo-validate-doc-loc tool
|
|
593
|
-
|
|
594
|
-
## Diagnostic Tools
|
|
595
|
-
|
|
596
|
-
### Test Connection
|
|
597
|
-
\`\`\`
|
|
598
|
-
Use: spo-test-connection
|
|
599
|
-
Purpose: Verify site accessibility and permissions
|
|
600
|
-
\`\`\`
|
|
601
|
-
|
|
602
|
-
### List Sites
|
|
603
|
-
\`\`\`
|
|
604
|
-
Use: spo-list-sites
|
|
605
|
-
Purpose: Verify configured sites and status
|
|
606
|
-
\`\`\`
|
|
607
|
-
|
|
608
|
-
### Validate Document Location
|
|
609
|
-
\`\`\`
|
|
610
|
-
Use: spo-validate-doc-loc
|
|
611
|
-
Purpose: Check PowerPlatform integration
|
|
612
|
-
\`\`\`
|
|
613
|
-
|
|
614
|
-
## Getting Help
|
|
615
|
-
|
|
616
|
-
If issues persist:
|
|
617
|
-
1. Check application logs for detailed error messages
|
|
618
|
-
2. Review audit logs in Azure AD
|
|
619
|
-
3. Test permissions using Microsoft Graph Explorer
|
|
620
|
-
4. Refer to SETUP.md for detailed configuration steps
|
|
621
|
-
|
|
622
|
-
For API-specific errors, refer to Microsoft Graph API documentation.
|
|
623
|
-
`;
|
|
624
|
-
return {
|
|
625
|
-
description: `SharePoint troubleshooting guide${errorType ? ` for ${errorType}` : ''}`,
|
|
626
|
-
messages: [
|
|
627
|
-
{
|
|
628
|
-
role: "user",
|
|
629
|
-
content: {
|
|
630
|
-
type: "text",
|
|
631
|
-
text: `Show SharePoint troubleshooting guide${errorType ? ` for ${errorType}` : ''}`,
|
|
632
|
-
},
|
|
633
|
-
},
|
|
634
|
-
{
|
|
635
|
-
role: "assistant",
|
|
636
|
-
content: {
|
|
637
|
-
type: "text",
|
|
638
|
-
text: guide,
|
|
639
|
-
},
|
|
640
|
-
},
|
|
641
|
-
],
|
|
642
|
-
};
|
|
643
|
-
});
|
|
644
|
-
server.prompt("spo-powerplatform-integration-health", {
|
|
645
|
-
entityName: z.string().optional().describe("Entity to check (e.g., 'account')"),
|
|
646
|
-
}, async ({ entityName }) => {
|
|
647
|
-
try {
|
|
648
|
-
const spoService = getSharePointService();
|
|
649
|
-
const ppService = getPowerPlatformService();
|
|
650
|
-
// Get all document locations for entity
|
|
651
|
-
const locations = await spoService.getCrmDocumentLocations(ppService, entityName);
|
|
652
|
-
// Analyze
|
|
653
|
-
const analysis = spoFormatters.analyzeCrmDocumentLocations(locations);
|
|
654
|
-
// Build health report
|
|
655
|
-
const sections = [];
|
|
656
|
-
sections.push('# 🏥 PowerPlatform-SharePoint Integration Health Check');
|
|
657
|
-
sections.push('');
|
|
658
|
-
if (entityName) {
|
|
659
|
-
sections.push(`**Entity:** ${entityName}`);
|
|
660
|
-
sections.push('');
|
|
661
|
-
}
|
|
662
|
-
sections.push('## Health Summary');
|
|
663
|
-
sections.push('');
|
|
664
|
-
analysis.insights.forEach(insight => {
|
|
665
|
-
sections.push(insight);
|
|
666
|
-
});
|
|
667
|
-
sections.push('');
|
|
668
|
-
sections.push('## Configured Document Locations');
|
|
669
|
-
sections.push(spoFormatters.formatCrmDocumentLocationsAsMarkdown(locations));
|
|
670
|
-
if (analysis.recommendations.length > 0) {
|
|
671
|
-
sections.push('');
|
|
672
|
-
sections.push('## Recommendations');
|
|
673
|
-
analysis.recommendations.forEach(rec => {
|
|
674
|
-
sections.push(`- 💡 ${rec}`);
|
|
675
|
-
});
|
|
676
|
-
}
|
|
677
|
-
sections.push('');
|
|
678
|
-
sections.push('## Next Steps');
|
|
679
|
-
sections.push('');
|
|
680
|
-
sections.push('1. Use `spo-validate-doc-loc` to validate individual locations');
|
|
681
|
-
sections.push('2. Check for missing or inaccessible folders');
|
|
682
|
-
sections.push('3. Verify service principal has access to all sites');
|
|
683
|
-
sections.push('4. Review empty folders and upload documents');
|
|
684
|
-
return {
|
|
685
|
-
description: `Integration health check${entityName ? ` for ${entityName}` : ''}`,
|
|
686
|
-
messages: [
|
|
687
|
-
{
|
|
688
|
-
role: "user",
|
|
689
|
-
content: {
|
|
690
|
-
type: "text",
|
|
691
|
-
text: `Check PowerPlatform-SharePoint integration health${entityName ? ` for ${entityName}` : ''}`,
|
|
692
|
-
},
|
|
693
|
-
},
|
|
694
|
-
{
|
|
695
|
-
role: "assistant",
|
|
696
|
-
content: {
|
|
697
|
-
type: "text",
|
|
698
|
-
text: sections.join('\n'),
|
|
699
|
-
},
|
|
700
|
-
},
|
|
701
|
-
],
|
|
702
|
-
};
|
|
703
|
-
}
|
|
704
|
-
catch (error) {
|
|
705
|
-
console.error("Error checking integration health:", error);
|
|
706
|
-
throw error;
|
|
707
|
-
}
|
|
708
|
-
});
|
|
709
|
-
// ========================================
|
|
710
|
-
// TOOLS
|
|
711
|
-
// ========================================
|
|
712
|
-
server.tool("spo-list-sites", "List all configured SharePoint sites (active and inactive)", {}, async () => {
|
|
713
|
-
try {
|
|
714
|
-
const service = getSharePointService();
|
|
715
|
-
const sites = service.getAllSites();
|
|
716
|
-
return {
|
|
717
|
-
content: [{
|
|
718
|
-
type: "text",
|
|
719
|
-
text: JSON.stringify(sites, null, 2),
|
|
720
|
-
}],
|
|
721
|
-
};
|
|
722
|
-
}
|
|
723
|
-
catch (error) {
|
|
724
|
-
console.error("Error listing SharePoint sites:", error);
|
|
725
|
-
return {
|
|
726
|
-
content: [{
|
|
727
|
-
type: "text",
|
|
728
|
-
text: `Failed to list sites: ${error.message}`,
|
|
729
|
-
}],
|
|
730
|
-
isError: true,
|
|
731
|
-
};
|
|
732
|
-
}
|
|
733
|
-
});
|
|
734
|
-
server.tool("spo-get-site-info", "Get detailed site information including metadata, created/modified dates, and owner info", {
|
|
735
|
-
siteId: z.string().describe("Site ID from configuration (use spo-list-sites to find IDs)"),
|
|
736
|
-
}, async ({ siteId }) => {
|
|
737
|
-
try {
|
|
738
|
-
const service = getSharePointService();
|
|
739
|
-
const siteInfo = await service.getSiteInfo(siteId);
|
|
740
|
-
return {
|
|
741
|
-
content: [{
|
|
742
|
-
type: "text",
|
|
743
|
-
text: JSON.stringify(siteInfo, null, 2),
|
|
744
|
-
}],
|
|
745
|
-
};
|
|
746
|
-
}
|
|
747
|
-
catch (error) {
|
|
748
|
-
console.error("Error getting SharePoint site info:", error);
|
|
749
|
-
return {
|
|
750
|
-
content: [{
|
|
751
|
-
type: "text",
|
|
752
|
-
text: `Failed to get site info: ${error.message}`,
|
|
753
|
-
}],
|
|
754
|
-
isError: true,
|
|
755
|
-
};
|
|
756
|
-
}
|
|
757
|
-
});
|
|
758
|
-
server.tool("spo-test-connection", "Test connectivity to a SharePoint site and verify permissions (Sites.Read.All and Files.Read.All required)", {
|
|
759
|
-
siteId: z.string().describe("Site ID from configuration"),
|
|
760
|
-
}, async ({ siteId }) => {
|
|
761
|
-
try {
|
|
762
|
-
const service = getSharePointService();
|
|
763
|
-
const result = await service.testConnection(siteId);
|
|
764
|
-
return {
|
|
765
|
-
content: [{
|
|
766
|
-
type: "text",
|
|
767
|
-
text: JSON.stringify(result, null, 2),
|
|
768
|
-
}],
|
|
769
|
-
};
|
|
770
|
-
}
|
|
771
|
-
catch (error) {
|
|
772
|
-
console.error("Error testing SharePoint connection:", error);
|
|
773
|
-
return {
|
|
774
|
-
content: [{
|
|
775
|
-
type: "text",
|
|
776
|
-
text: `Failed to test connection: ${error.message}`,
|
|
777
|
-
}],
|
|
778
|
-
isError: true,
|
|
779
|
-
};
|
|
780
|
-
}
|
|
781
|
-
});
|
|
782
|
-
server.tool("spo-list-drives", "List all document libraries (drives) in a SharePoint site with metadata", {
|
|
783
|
-
siteId: z.string().describe("Site ID from configuration"),
|
|
784
|
-
}, async ({ siteId }) => {
|
|
785
|
-
try {
|
|
786
|
-
const service = getSharePointService();
|
|
787
|
-
const drives = await service.listDrives(siteId);
|
|
788
|
-
return {
|
|
789
|
-
content: [{
|
|
790
|
-
type: "text",
|
|
791
|
-
text: JSON.stringify(drives, null, 2),
|
|
792
|
-
}],
|
|
793
|
-
};
|
|
794
|
-
}
|
|
795
|
-
catch (error) {
|
|
796
|
-
console.error("Error listing SharePoint drives:", error);
|
|
797
|
-
return {
|
|
798
|
-
content: [{
|
|
799
|
-
type: "text",
|
|
800
|
-
text: `Failed to list drives: ${error.message}`,
|
|
801
|
-
}],
|
|
802
|
-
isError: true,
|
|
803
|
-
};
|
|
804
|
-
}
|
|
805
|
-
});
|
|
806
|
-
server.tool("spo-get-drive-info", "Get detailed document library information including quota, owner, and created/modified dates", {
|
|
807
|
-
siteId: z.string().describe("Site ID from configuration"),
|
|
808
|
-
driveId: z.string().describe("Drive ID (use spo-list-drives to find IDs)"),
|
|
809
|
-
}, async ({ siteId, driveId }) => {
|
|
810
|
-
try {
|
|
811
|
-
const service = getSharePointService();
|
|
812
|
-
const driveInfo = await service.getDriveInfo(siteId, driveId);
|
|
813
|
-
return {
|
|
814
|
-
content: [{
|
|
815
|
-
type: "text",
|
|
816
|
-
text: JSON.stringify(driveInfo, null, 2),
|
|
817
|
-
}],
|
|
818
|
-
};
|
|
819
|
-
}
|
|
820
|
-
catch (error) {
|
|
821
|
-
console.error("Error getting SharePoint drive info:", error);
|
|
822
|
-
return {
|
|
823
|
-
content: [{
|
|
824
|
-
type: "text",
|
|
825
|
-
text: `Failed to get drive info: ${error.message}`,
|
|
826
|
-
}],
|
|
827
|
-
isError: true,
|
|
828
|
-
};
|
|
829
|
-
}
|
|
830
|
-
});
|
|
831
|
-
server.tool("spo-clear-cache", "Clear cached SharePoint responses (useful after site changes or for troubleshooting)", {
|
|
832
|
-
siteId: z.string().optional().describe("Clear cache for specific site only (optional)"),
|
|
833
|
-
pattern: z.string().optional().describe("Clear only cache entries matching this pattern (optional)"),
|
|
834
|
-
}, async ({ siteId, pattern }) => {
|
|
835
|
-
try {
|
|
836
|
-
const service = getSharePointService();
|
|
837
|
-
const clearedCount = service.clearCache(pattern, siteId);
|
|
838
|
-
return {
|
|
839
|
-
content: [{
|
|
840
|
-
type: "text",
|
|
841
|
-
text: JSON.stringify({ clearedCount, message: `Cleared ${clearedCount} cache entries` }, null, 2),
|
|
842
|
-
}],
|
|
843
|
-
};
|
|
844
|
-
}
|
|
845
|
-
catch (error) {
|
|
846
|
-
console.error("Error clearing SharePoint cache:", error);
|
|
847
|
-
return {
|
|
848
|
-
content: [{
|
|
849
|
-
type: "text",
|
|
850
|
-
text: `Failed to clear cache: ${error.message}`,
|
|
851
|
-
}],
|
|
852
|
-
isError: true,
|
|
853
|
-
};
|
|
854
|
-
}
|
|
855
|
-
});
|
|
856
|
-
server.tool("spo-list-items", "List all files and folders in a document library or folder", {
|
|
857
|
-
siteId: z.string().describe("Site ID from configuration"),
|
|
858
|
-
driveId: z.string().describe("Drive ID"),
|
|
859
|
-
folderId: z.string().optional().describe("Folder ID (optional, defaults to root)"),
|
|
860
|
-
}, async ({ siteId, driveId, folderId }) => {
|
|
861
|
-
try {
|
|
862
|
-
const service = getSharePointService();
|
|
863
|
-
const items = await service.listItems(siteId, driveId, folderId);
|
|
864
|
-
return {
|
|
865
|
-
content: [{
|
|
866
|
-
type: "text",
|
|
867
|
-
text: JSON.stringify(items, null, 2),
|
|
868
|
-
}],
|
|
869
|
-
};
|
|
870
|
-
}
|
|
871
|
-
catch (error) {
|
|
872
|
-
console.error("Error listing SharePoint items:", error);
|
|
873
|
-
return {
|
|
874
|
-
content: [{
|
|
875
|
-
type: "text",
|
|
876
|
-
text: `Failed to list items: ${error.message}`,
|
|
877
|
-
}],
|
|
878
|
-
isError: true,
|
|
879
|
-
};
|
|
880
|
-
}
|
|
881
|
-
});
|
|
882
|
-
server.tool("spo-get-item", "Get detailed file or folder metadata by ID", {
|
|
883
|
-
siteId: z.string().describe("Site ID from configuration"),
|
|
884
|
-
driveId: z.string().describe("Drive ID"),
|
|
885
|
-
itemId: z.string().describe("Item ID"),
|
|
886
|
-
}, async ({ siteId, driveId, itemId }) => {
|
|
887
|
-
try {
|
|
888
|
-
const service = getSharePointService();
|
|
889
|
-
const item = await service.getItem(siteId, driveId, itemId);
|
|
890
|
-
return {
|
|
891
|
-
content: [{
|
|
892
|
-
type: "text",
|
|
893
|
-
text: JSON.stringify(item, null, 2),
|
|
894
|
-
}],
|
|
895
|
-
};
|
|
896
|
-
}
|
|
897
|
-
catch (error) {
|
|
898
|
-
console.error("Error getting SharePoint item:", error);
|
|
899
|
-
return {
|
|
900
|
-
content: [{
|
|
901
|
-
type: "text",
|
|
902
|
-
text: `Failed to get item: ${error.message}`,
|
|
903
|
-
}],
|
|
904
|
-
isError: true,
|
|
905
|
-
};
|
|
906
|
-
}
|
|
907
|
-
});
|
|
908
|
-
server.tool("spo-get-item-by-path", "Get file or folder metadata by path (relative to drive root)", {
|
|
909
|
-
siteId: z.string().describe("Site ID from configuration"),
|
|
910
|
-
driveId: z.string().describe("Drive ID"),
|
|
911
|
-
path: z.string().describe("Item path (e.g., '/folder/file.docx' or 'folder/subfolder')"),
|
|
912
|
-
}, async ({ siteId, driveId, path }) => {
|
|
913
|
-
try {
|
|
914
|
-
const service = getSharePointService();
|
|
915
|
-
const item = await service.getItemByPath(siteId, driveId, path);
|
|
916
|
-
return {
|
|
917
|
-
content: [{
|
|
918
|
-
type: "text",
|
|
919
|
-
text: JSON.stringify(item, null, 2),
|
|
920
|
-
}],
|
|
921
|
-
};
|
|
922
|
-
}
|
|
923
|
-
catch (error) {
|
|
924
|
-
console.error("Error getting SharePoint item by path:", error);
|
|
925
|
-
return {
|
|
926
|
-
content: [{
|
|
927
|
-
type: "text",
|
|
928
|
-
text: `Failed to get item by path: ${error.message}`,
|
|
929
|
-
}],
|
|
930
|
-
isError: true,
|
|
931
|
-
};
|
|
932
|
-
}
|
|
933
|
-
});
|
|
934
|
-
server.tool("spo-search-items", "Search for files by filename or metadata (filename and metadata search only, not full-text)", {
|
|
935
|
-
siteId: z.string().describe("Site ID from configuration"),
|
|
936
|
-
query: z.string().describe("Search query"),
|
|
937
|
-
driveId: z.string().optional().describe("Limit search to specific drive (optional)"),
|
|
938
|
-
limit: z.number().optional().describe("Maximum results (default: 100, max configured in SHAREPOINT_MAX_SEARCH_RESULTS)"),
|
|
939
|
-
}, async ({ siteId, query, driveId, limit }) => {
|
|
940
|
-
try {
|
|
941
|
-
const service = getSharePointService();
|
|
942
|
-
const result = await service.searchItems(siteId, query, driveId, limit);
|
|
943
|
-
return {
|
|
944
|
-
content: [{
|
|
945
|
-
type: "text",
|
|
946
|
-
text: JSON.stringify(result, null, 2),
|
|
947
|
-
}],
|
|
948
|
-
};
|
|
949
|
-
}
|
|
950
|
-
catch (error) {
|
|
951
|
-
console.error("Error searching SharePoint items:", error);
|
|
952
|
-
return {
|
|
953
|
-
content: [{
|
|
954
|
-
type: "text",
|
|
955
|
-
text: `Failed to search items: ${error.message}`,
|
|
956
|
-
}],
|
|
957
|
-
isError: true,
|
|
958
|
-
};
|
|
959
|
-
}
|
|
960
|
-
});
|
|
961
|
-
server.tool("spo-get-recent-items", "Get recently modified items in a document library", {
|
|
962
|
-
siteId: z.string().describe("Site ID from configuration"),
|
|
963
|
-
driveId: z.string().describe("Drive ID"),
|
|
964
|
-
limit: z.number().optional().describe("Maximum results (default: 20, max: 100)"),
|
|
965
|
-
days: z.number().optional().describe("Days back to search (default: 30)"),
|
|
966
|
-
}, async ({ siteId, driveId, limit, days }) => {
|
|
967
|
-
try {
|
|
968
|
-
const service = getSharePointService();
|
|
969
|
-
const items = await service.getRecentItems(siteId, driveId, limit, days);
|
|
970
|
-
return {
|
|
971
|
-
content: [{
|
|
972
|
-
type: "text",
|
|
973
|
-
text: JSON.stringify(items, null, 2),
|
|
974
|
-
}],
|
|
975
|
-
};
|
|
976
|
-
}
|
|
977
|
-
catch (error) {
|
|
978
|
-
console.error("Error getting recent SharePoint items:", error);
|
|
979
|
-
return {
|
|
980
|
-
content: [{
|
|
981
|
-
type: "text",
|
|
982
|
-
text: `Failed to get recent items: ${error.message}`,
|
|
983
|
-
}],
|
|
984
|
-
isError: true,
|
|
985
|
-
};
|
|
986
|
-
}
|
|
987
|
-
});
|
|
988
|
-
server.tool("spo-get-folder-structure", "Get recursive folder tree structure (useful for understanding site organization)", {
|
|
989
|
-
siteId: z.string().describe("Site ID from configuration"),
|
|
990
|
-
driveId: z.string().describe("Drive ID"),
|
|
991
|
-
folderId: z.string().optional().describe("Root folder ID (optional, defaults to drive root)"),
|
|
992
|
-
depth: z.number().optional().describe("Recursion depth (default: 3, max: 10)"),
|
|
993
|
-
}, async ({ siteId, driveId, folderId, depth }) => {
|
|
994
|
-
try {
|
|
995
|
-
const service = getSharePointService();
|
|
996
|
-
const tree = await service.getFolderStructure(siteId, driveId, folderId, depth);
|
|
997
|
-
return {
|
|
998
|
-
content: [{
|
|
999
|
-
type: "text",
|
|
1000
|
-
text: JSON.stringify(tree, null, 2),
|
|
1001
|
-
}],
|
|
1002
|
-
};
|
|
1003
|
-
}
|
|
1004
|
-
catch (error) {
|
|
1005
|
-
console.error("Error getting SharePoint folder structure:", error);
|
|
1006
|
-
return {
|
|
1007
|
-
content: [{
|
|
1008
|
-
type: "text",
|
|
1009
|
-
text: `Failed to get folder structure: ${error.message}`,
|
|
1010
|
-
}],
|
|
1011
|
-
isError: true,
|
|
1012
|
-
};
|
|
1013
|
-
}
|
|
1014
|
-
});
|
|
1015
|
-
server.tool("spo-get-crm-doc-locs", "Get SharePoint document locations configured in PowerPlatform Dataverse (sharepointdocumentlocation entity)", {
|
|
1016
|
-
entityName: z.string().optional().describe("Filter by entity logical name (e.g., 'account', 'contact')"),
|
|
1017
|
-
recordId: z.string().optional().describe("Filter by specific record ID (GUID)"),
|
|
1018
|
-
}, async ({ entityName, recordId }) => {
|
|
1019
|
-
try {
|
|
1020
|
-
const spoService = getSharePointService();
|
|
1021
|
-
const ppService = getPowerPlatformService();
|
|
1022
|
-
const locations = await spoService.getCrmDocumentLocations(ppService, entityName, recordId);
|
|
1023
|
-
return {
|
|
1024
|
-
content: [{
|
|
1025
|
-
type: "text",
|
|
1026
|
-
text: JSON.stringify(locations, null, 2),
|
|
1027
|
-
}],
|
|
1028
|
-
};
|
|
1029
|
-
}
|
|
1030
|
-
catch (error) {
|
|
1031
|
-
console.error("Error getting CRM document locations:", error);
|
|
1032
|
-
return {
|
|
1033
|
-
content: [{
|
|
1034
|
-
type: "text",
|
|
1035
|
-
text: `Failed to get CRM document locations: ${error.message}`,
|
|
1036
|
-
}],
|
|
1037
|
-
isError: true,
|
|
1038
|
-
};
|
|
1039
|
-
}
|
|
1040
|
-
});
|
|
1041
|
-
server.tool("spo-validate-doc-loc", "Validate that a PowerPlatform document location configuration matches the actual SharePoint site structure. Checks site accessibility, folder existence, and file counts. Returns validation status (valid/warning/error) with issues and recommendations.", {
|
|
1042
|
-
documentLocationId: z.string().describe("GUID of the sharepointdocumentlocation record in PowerPlatform"),
|
|
1043
|
-
}, async ({ documentLocationId }) => {
|
|
1044
|
-
try {
|
|
1045
|
-
const spoService = getSharePointService();
|
|
1046
|
-
const ppService = getPowerPlatformService();
|
|
1047
|
-
const result = await spoService.validateDocumentLocation(ppService, documentLocationId);
|
|
1048
|
-
return {
|
|
1049
|
-
content: [{
|
|
1050
|
-
type: "text",
|
|
1051
|
-
text: JSON.stringify(result, null, 2),
|
|
1052
|
-
}],
|
|
1053
|
-
};
|
|
1054
|
-
}
|
|
1055
|
-
catch (error) {
|
|
1056
|
-
console.error("Error validating document location:", error);
|
|
1057
|
-
return {
|
|
1058
|
-
content: [{
|
|
1059
|
-
type: "text",
|
|
1060
|
-
text: `Failed to validate document location: ${error.message}`,
|
|
1061
|
-
}],
|
|
1062
|
-
isError: true,
|
|
1063
|
-
};
|
|
1064
|
-
}
|
|
1065
|
-
});
|
|
1066
|
-
server.tool("spo-verify-doc-mig", "Verify that documents were successfully migrated from source to target SharePoint folder. Compares file counts, sizes, names, and modified dates. Returns migration status (complete/incomplete/failed) with success rate and detailed comparison.", {
|
|
1067
|
-
sourceSiteId: z.string().describe("Source SharePoint site ID"),
|
|
1068
|
-
sourcePath: z.string().describe("Source folder path (e.g., '/Documents/Archive')"),
|
|
1069
|
-
targetSiteId: z.string().describe("Target SharePoint site ID"),
|
|
1070
|
-
targetPath: z.string().describe("Target folder path (e.g., '/NewLibrary/Archive')"),
|
|
1071
|
-
}, async ({ sourceSiteId, sourcePath, targetSiteId, targetPath }) => {
|
|
1072
|
-
try {
|
|
1073
|
-
const spoService = getSharePointService();
|
|
1074
|
-
const ppService = getPowerPlatformService();
|
|
1075
|
-
const result = await spoService.verifyDocumentMigration(ppService, sourceSiteId, sourcePath, targetSiteId, targetPath);
|
|
1076
|
-
return {
|
|
1077
|
-
content: [{
|
|
1078
|
-
type: "text",
|
|
1079
|
-
text: JSON.stringify(result, null, 2),
|
|
1080
|
-
}],
|
|
1081
|
-
};
|
|
1082
|
-
}
|
|
1083
|
-
catch (error) {
|
|
1084
|
-
console.error("Error verifying document migration:", error);
|
|
1085
|
-
return {
|
|
1086
|
-
content: [{
|
|
1087
|
-
type: "text",
|
|
1088
|
-
text: `Failed to verify document migration: ${error.message}`,
|
|
1089
|
-
}],
|
|
1090
|
-
isError: true,
|
|
1091
|
-
};
|
|
85
|
+
}
|
|
86
|
+
function checkDeleteEnabled() {
|
|
87
|
+
if (process.env.SHAREPOINT_ENABLE_DELETE !== 'true') {
|
|
88
|
+
throw new Error('Delete operations are disabled. Set SHAREPOINT_ENABLE_DELETE=true to enable.');
|
|
1092
89
|
}
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
|
|
90
|
+
}
|
|
91
|
+
// Register all prompts and tools
|
|
92
|
+
registerSharePointPrompts(server, getSharePointService, getPowerPlatformService);
|
|
93
|
+
registerReadTools(server, getSharePointService, getFileOperationsService, getPowerPlatformService);
|
|
94
|
+
registerWriteTools(server, getSharePointService, getFileOperationsService, checkWriteEnabled, checkDeleteEnabled);
|
|
95
|
+
// Log tool counts
|
|
96
|
+
const writeEnabled = process.env.SHAREPOINT_ENABLE_WRITE === 'true';
|
|
97
|
+
const deleteEnabled = process.env.SHAREPOINT_ENABLE_DELETE === 'true';
|
|
98
|
+
const writeToolCount = writeEnabled ? 5 : 0;
|
|
99
|
+
const deleteToolCount = deleteEnabled ? 1 : 0;
|
|
100
|
+
const totalTools = 16 + writeToolCount + deleteToolCount; // 15 existing + 1 download + write + delete
|
|
101
|
+
console.error(`SharePoint tools registered: ${totalTools} tools (${writeToolCount + deleteToolCount} write), 10 prompts`);
|
|
1096
102
|
}
|
|
1097
103
|
// CLI entry point (standalone execution)
|
|
1098
104
|
// Uses realpathSync to resolve symlinks created by npx
|