delegate-sf-mcp 0.2.0

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.
Files changed (44) hide show
  1. package/.eslintrc.json +20 -0
  2. package/LICENSE +24 -0
  3. package/README.md +76 -0
  4. package/auth.js +148 -0
  5. package/bin/config-helper.js +51 -0
  6. package/bin/mcp-salesforce.js +12 -0
  7. package/bin/setup.js +266 -0
  8. package/bin/status.js +134 -0
  9. package/docs/README.md +52 -0
  10. package/docs/step1.png +0 -0
  11. package/docs/step2.png +0 -0
  12. package/docs/step3.png +0 -0
  13. package/docs/step4.png +0 -0
  14. package/examples/README.md +35 -0
  15. package/package.json +16 -0
  16. package/scripts/README.md +30 -0
  17. package/src/auth/file-storage.js +447 -0
  18. package/src/auth/oauth.js +417 -0
  19. package/src/auth/token-manager.js +207 -0
  20. package/src/backup/manager.js +949 -0
  21. package/src/index.js +168 -0
  22. package/src/salesforce/client.js +388 -0
  23. package/src/sf-client.js +79 -0
  24. package/src/tools/auth.js +190 -0
  25. package/src/tools/backup.js +486 -0
  26. package/src/tools/create.js +109 -0
  27. package/src/tools/delegate-hygiene.js +268 -0
  28. package/src/tools/delegate-validate.js +212 -0
  29. package/src/tools/delegate-verify.js +143 -0
  30. package/src/tools/delete.js +72 -0
  31. package/src/tools/describe.js +132 -0
  32. package/src/tools/installation-info.js +656 -0
  33. package/src/tools/learn-context.js +1077 -0
  34. package/src/tools/learn.js +351 -0
  35. package/src/tools/query.js +82 -0
  36. package/src/tools/repair-credentials.js +77 -0
  37. package/src/tools/setup.js +120 -0
  38. package/src/tools/time_machine.js +347 -0
  39. package/src/tools/update.js +138 -0
  40. package/src/tools.js +214 -0
  41. package/src/utils/cache.js +120 -0
  42. package/src/utils/debug.js +52 -0
  43. package/src/utils/logger.js +19 -0
  44. package/tokens.json +8 -0
@@ -0,0 +1,656 @@
1
+ /**
2
+ * Salesforce Installation Info Tool
3
+ *
4
+ * This tool provides information about the learned Salesforce installation,
5
+ * including objects, fields, and customizations.
6
+ */
7
+
8
+ import { getInstallationDocumentation, hasInstallationDocumentation } from './learn.js';
9
+ import { getUserContext } from './learn-context.js';
10
+ import { logger } from '../utils/debug.js';
11
+
12
+ export const salesforceInstallationInfoTool = {
13
+ name: "salesforce_installation_info",
14
+ description: "Returns comprehensive information about the learned Salesforce installation, including all object details, field specifications, relationships, permissions, and customizations from the learned installation data.",
15
+ inputSchema: {
16
+ type: "object",
17
+ properties: {
18
+ object_name: {
19
+ type: "string",
20
+ description: "Specific object for detailed information (optional). Example: 'Account', 'Contact', 'CustomObject__c'"
21
+ },
22
+ field_search: {
23
+ type: "string",
24
+ description: "Search for fields with this name or label (optional)"
25
+ },
26
+ show_custom_only: {
27
+ type: "boolean",
28
+ description: "Show only custom objects and custom fields",
29
+ default: false
30
+ },
31
+ include_relationships: {
32
+ type: "boolean",
33
+ description: "Include relationship information between objects",
34
+ default: true
35
+ },
36
+ detailed_fields: {
37
+ type: "boolean",
38
+ description: "Show detailed field information including data types, constraints, and metadata",
39
+ default: true
40
+ },
41
+ include_permissions: {
42
+ type: "boolean",
43
+ description: "Include detailed permission information for objects and fields",
44
+ default: true
45
+ },
46
+ max_fields_per_object: {
47
+ type: "number",
48
+ description: "Maximum number of fields to show per object (default: all fields)",
49
+ default: 0
50
+ }
51
+ }
52
+ }
53
+ };
54
+
55
+ export async function handleSalesforceInstallationInfo(args) {
56
+ const {
57
+ object_name,
58
+ field_search,
59
+ show_custom_only = false,
60
+ include_relationships = true,
61
+ detailed_fields = true,
62
+ include_permissions = true,
63
+ max_fields_per_object = 0
64
+ } = args;
65
+
66
+ try {
67
+ // Check if installation has been learned
68
+ const hasDocumentation = await hasInstallationDocumentation();
69
+ if (!hasDocumentation) {
70
+ return {
71
+ content: [{
72
+ type: "text",
73
+ text: `⚠️ **Salesforce installation not yet learned**\n\n` +
74
+ `The Salesforce installation has not been analyzed and documented yet.\n\n` +
75
+ `🚀 **Solution:** First run the \`salesforce_learn\` tool:\n` +
76
+ `\`\`\`\n` +
77
+ `{\n` +
78
+ ` "tool": "salesforce_learn"\n` +
79
+ `}\n` +
80
+ `\`\`\`\n\n` +
81
+ `Then I can provide detailed information about your Salesforce installation.`
82
+ }]
83
+ };
84
+ }
85
+
86
+ const documentation = await getInstallationDocumentation();
87
+
88
+ // Handle specific object request
89
+ if (object_name) {
90
+ return handleSpecificObjectInfo(object_name, documentation, {
91
+ include_relationships,
92
+ detailed_fields,
93
+ include_permissions,
94
+ max_fields_per_object
95
+ });
96
+ }
97
+
98
+ // Handle field search
99
+ if (field_search) {
100
+ return handleFieldSearch(field_search, documentation, {
101
+ show_custom_only,
102
+ detailed_fields,
103
+ max_fields_per_object
104
+ });
105
+ }
106
+
107
+ // General installation overview
108
+ return await handleGeneralOverview(documentation, {
109
+ show_custom_only,
110
+ include_relationships,
111
+ detailed_fields,
112
+ include_permissions
113
+ });
114
+
115
+ } catch (error) {
116
+ logger.error('❌ Error retrieving installation info:', error);
117
+ return {
118
+ content: [{
119
+ type: "text",
120
+ text: `❌ **Error retrieving installation information:**\n\n${error.message}`
121
+ }]
122
+ };
123
+ }
124
+ }
125
+
126
+ function handleSpecificObjectInfo(objectName, documentation, options) {
127
+ const { include_relationships, detailed_fields, include_permissions, max_fields_per_object } = options;
128
+
129
+ const obj = documentation.objects[objectName];
130
+ if (!obj) {
131
+ // Try to find similar object names
132
+ const availableObjects = Object.keys(documentation.objects);
133
+ const similarObjects = availableObjects.filter(name =>
134
+ name.toLowerCase().includes(objectName.toLowerCase()) ||
135
+ documentation.objects[name].basic_info?.label?.toLowerCase().includes(objectName.toLowerCase())
136
+ );
137
+
138
+ let suggestion = '';
139
+ if (similarObjects.length > 0) {
140
+ suggestion = `\n\n🔍 **Similar objects found:**\n${similarObjects.slice(0, 5).map(name => `- ${name} (${documentation.objects[name].basic_info?.label})`).join('\n')}`;
141
+ }
142
+
143
+ return {
144
+ content: [{
145
+ type: "text",
146
+ text: `❌ **Object '${objectName}' not found**\n\n` +
147
+ `The object does not exist in the learned Salesforce installation.${suggestion}\n\n` +
148
+ `💡 Use the tool without parameters to see all available objects.`
149
+ }]
150
+ };
151
+ }
152
+
153
+ if (obj.error) {
154
+ return {
155
+ content: [{
156
+ type: "text",
157
+ text: `⚠️ **Object '${objectName}' - Analysis Error**\n\n${obj.error}`
158
+ }]
159
+ };
160
+ }
161
+
162
+ let result = `📋 **${obj.basic_info.label} (${obj.basic_info.name})**\n\n`;
163
+
164
+ // Basic info with all available details
165
+ result += `## 📝 Basic Information\n`;
166
+ result += `- **API Name:** \`${obj.basic_info.name}\`\n`;
167
+ result += `- **Label:** ${obj.basic_info.label}\n`;
168
+ result += `- **Plural Label:** ${obj.basic_info.label_plural}\n`;
169
+ result += `- **Custom Object:** ${obj.basic_info.custom ? '✅ Yes' : '❌ No'}\n`;
170
+
171
+ // Extended metadata if available
172
+ if (obj.metadata) {
173
+ result += `\n## 🔐 Permissions & Capabilities\n`;
174
+ result += `- **Createable:** ${obj.metadata.createable ? '✅' : '❌'}\n`;
175
+ result += `- **Updateable:** ${obj.metadata.updateable ? '✅' : '❌'}\n`;
176
+ result += `- **Deletable:** ${obj.metadata.deletable ? '✅' : '❌'}\n`;
177
+ result += `- **Queryable:** ${obj.metadata.queryable ? '✅' : '❌'}\n`;
178
+ if (obj.metadata.searchable !== undefined) {
179
+ result += `- **Searchable:** ${obj.metadata.searchable ? '✅' : '❌'}\n`;
180
+ }
181
+ if (obj.metadata.retrieveable !== undefined) {
182
+ result += `- **Retrieveable:** ${obj.metadata.retrieveable ? '✅' : '❌'}\n`;
183
+ }
184
+ }
185
+
186
+ // Fields overview with detailed statistics
187
+ result += `\n## 📊 Fields Overview\n`;
188
+ result += `- **Total Fields:** ${obj.field_count}\n`;
189
+ result += `- **Custom Fields:** ${obj.custom_field_count}\n`;
190
+ result += `- **Standard Fields:** ${obj.field_count - obj.custom_field_count}\n`;
191
+
192
+ // Detailed field information
193
+ if (detailed_fields && obj.fields) {
194
+ result += `\n## 📋 Detailed Field Information\n`;
195
+
196
+ const fieldEntries = Object.entries(obj.fields);
197
+ const fieldsToShow = max_fields_per_object > 0 ? fieldEntries.slice(0, max_fields_per_object) : fieldEntries;
198
+
199
+ // Group fields by type for better organization
200
+ const fieldsByType = {};
201
+ for (const [fieldName, field] of fieldsToShow) {
202
+ const type = field.type || 'unknown';
203
+ if (!fieldsByType[type]) {
204
+ fieldsByType[type] = [];
205
+ }
206
+ fieldsByType[type].push([fieldName, field]);
207
+ }
208
+
209
+ for (const [fieldType, fields] of Object.entries(fieldsByType)) {
210
+ result += `\n### ${fieldType.toUpperCase()} Fields (${fields.length})\n`;
211
+
212
+ for (const [fieldName, field] of fields) {
213
+ result += `\n**${field.label || fieldName}** (\`${fieldName}\`)\n`;
214
+ result += `- **Type:** ${field.type}\n`;
215
+ result += `- **Required:** ${field.required ? '✅ Yes' : '❌ No'}\n`;
216
+ result += `- **Custom:** ${field.custom ? '✅ Yes' : '❌ No'}\n`;
217
+ result += `- **Updateable:** ${field.updateable ? '✅' : '❌'}\n`;
218
+ result += `- **Createable:** ${field.createable ? '✅' : '❌'}\n`;
219
+
220
+ // Enhanced writability information
221
+ if (field.writability) {
222
+ const writabilityStatus = getWritabilityStatus(field.writability);
223
+ result += `- **Writability:** ${writabilityStatus}\n`;
224
+ }
225
+
226
+ // Type-specific information
227
+ if (field.max_length) {
228
+ result += `- **Max Length:** ${field.max_length}\n`;
229
+ }
230
+ if (field.precision !== undefined) {
231
+ result += `- **Precision:** ${field.precision}\n`;
232
+ }
233
+ if (field.scale !== undefined) {
234
+ result += `- **Scale:** ${field.scale}\n`;
235
+ }
236
+ if (field.picklist_values && field.picklist_values.length > 0) {
237
+ result += `- **Picklist Values:** ${field.picklist_values.slice(0, 5).map(v => `"${v}"`).join(', ')}`;
238
+ if (field.picklist_values.length > 5) {
239
+ result += ` (and ${field.picklist_values.length - 5} more)`;
240
+ }
241
+ result += `\n`;
242
+ }
243
+ if (field.references && field.references.length > 0) {
244
+ result += `- **References:** ${field.references.join(', ')}\n`;
245
+ }
246
+ if (field.default_value !== undefined && field.default_value !== null) {
247
+ result += `- **Default Value:** ${field.default_value}\n`;
248
+ }
249
+ if (field.help_text) {
250
+ result += `- **Help Text:** ${field.help_text}\n`;
251
+ }
252
+ }
253
+ }
254
+
255
+ if (max_fields_per_object > 0 && fieldEntries.length > max_fields_per_object) {
256
+ result += `\n*... and ${fieldEntries.length - max_fields_per_object} more fields*\n`;
257
+ }
258
+ }
259
+
260
+ // Relationships with complete details
261
+ if (include_relationships && obj.relationships) {
262
+ result += `\n## 🔗 Relationships\n`;
263
+
264
+ if (obj.relationships.parent_relationships && obj.relationships.parent_relationships.length > 0) {
265
+ result += `\n### Parent Relationships (${obj.relationships.parent_relationships.length})\n`;
266
+ for (const rel of obj.relationships.parent_relationships) {
267
+ result += `- **${rel.field}** → References: ${rel.references.join(', ')}\n`;
268
+ }
269
+ }
270
+
271
+ if (obj.relationships.child_relationships && obj.relationships.child_relationships.length > 0) {
272
+ result += `\n### Child Relationships (${obj.relationships.child_relationships.length})\n`;
273
+ for (const rel of obj.relationships.child_relationships) {
274
+ result += `- **${rel.child_object}** (Relationship: ${rel.relationship_name})\n`;
275
+ if (rel.field) {
276
+ result += ` - Field: ${rel.field}\n`;
277
+ }
278
+ }
279
+ }
280
+
281
+ if ((!obj.relationships.parent_relationships || obj.relationships.parent_relationships.length === 0) &&
282
+ (!obj.relationships.child_relationships || obj.relationships.child_relationships.length === 0)) {
283
+ result += `\n*No relationships found for this object.*\n`;
284
+ }
285
+ }
286
+
287
+ return {
288
+ content: [{
289
+ type: "text",
290
+ text: result
291
+ }]
292
+ };
293
+ }
294
+
295
+ function handleFieldSearch(searchTerm, documentation, options) {
296
+ const { show_custom_only, detailed_fields, max_fields_per_object } = options;
297
+ const searchResults = [];
298
+
299
+ for (const [objectName, obj] of Object.entries(documentation.objects)) {
300
+ if (obj.error) continue;
301
+
302
+ for (const [fieldName, field] of Object.entries(obj.fields || {})) {
303
+ if (show_custom_only && !field.custom) continue;
304
+
305
+ const matchesName = fieldName.toLowerCase().includes(searchTerm.toLowerCase());
306
+ const matchesLabel = field.label?.toLowerCase().includes(searchTerm.toLowerCase());
307
+
308
+ if (matchesName || matchesLabel) {
309
+ searchResults.push({
310
+ object: objectName,
311
+ objectLabel: obj.basic_info.label,
312
+ field: fieldName,
313
+ fieldData: field
314
+ });
315
+ }
316
+ }
317
+ }
318
+
319
+ if (searchResults.length === 0) {
320
+ return {
321
+ content: [{
322
+ type: "text",
323
+ text: `🔍 **No fields found for: "${searchTerm}"**\n\n` +
324
+ `${show_custom_only ? 'When searching for custom fields, ' : ''}no matching fields were found.\n\n` +
325
+ `💡 **Tips:**\n` +
326
+ `- Try part of the field name\n` +
327
+ `- Search by label instead of API name\n` +
328
+ `- Use \`show_custom_only: false\` to also search standard fields`
329
+ }]
330
+ };
331
+ }
332
+
333
+ let result = `🔍 **Search results for "${searchTerm}"** (${searchResults.length} fields found)\n\n`;
334
+
335
+ const groupedResults = {};
336
+ for (const item of searchResults) {
337
+ if (!groupedResults[item.object]) {
338
+ groupedResults[item.object] = [];
339
+ }
340
+ groupedResults[item.object].push(item);
341
+ }
342
+
343
+ for (const [objectName, fields] of Object.entries(groupedResults)) {
344
+ const objectLabel = fields[0].objectLabel;
345
+ result += `## ${objectLabel} (${objectName})\n`;
346
+
347
+ const fieldsToShow = max_fields_per_object > 0 ? fields.slice(0, max_fields_per_object) : fields;
348
+
349
+ for (const item of fieldsToShow) {
350
+ const field = item.fieldData;
351
+ result += `\n### ${field.label || item.field} (\`${item.field}\`)\n`;
352
+
353
+ if (detailed_fields) {
354
+ result += `- **Type:** ${field.type}\n`;
355
+ result += `- **Required:** ${field.required ? '✅ Yes' : '❌ No'}\n`;
356
+ result += `- **Custom:** ${field.custom ? '✅ Yes' : '❌ No'}\n`;
357
+ result += `- **Updateable:** ${field.updateable ? '✅' : '❌'}\n`;
358
+ result += `- **Createable:** ${field.createable ? '✅' : '❌'}\n`;
359
+
360
+ // Enhanced writability information
361
+ if (field.writability) {
362
+ const writabilityStatus = getWritabilityStatus(field.writability);
363
+ result += `- **Writability:** ${writabilityStatus}\n`;
364
+ }
365
+
366
+ if (field.max_length) {
367
+ result += `- **Max Length:** ${field.max_length}\n`;
368
+ }
369
+ if (field.precision !== undefined) {
370
+ result += `- **Precision:** ${field.precision}\n`;
371
+ }
372
+ if (field.scale !== undefined) {
373
+ result += `- **Scale:** ${field.scale}\n`;
374
+ }
375
+ if (field.picklist_values && field.picklist_values.length > 0) {
376
+ result += `- **Picklist Values:** ${field.picklist_values.slice(0, 3).map(v => `"${v}"`).join(', ')}`;
377
+ if (field.picklist_values.length > 3) {
378
+ result += ` (and ${field.picklist_values.length - 3} more)`;
379
+ }
380
+ result += `\n`;
381
+ }
382
+ if (field.references && field.references.length > 0) {
383
+ result += `- **References:** ${field.references.join(', ')}\n`;
384
+ }
385
+ if (field.help_text) {
386
+ result += `- **Help Text:** ${field.help_text}\n`;
387
+ }
388
+ } else {
389
+ const custom = field.custom ? ' 🔧' : '';
390
+ const required = field.required ? ' ⚠️' : '';
391
+ result += `- **Type:** ${field.type}${required}${custom}\n`;
392
+ }
393
+ }
394
+
395
+ if (max_fields_per_object > 0 && fields.length > max_fields_per_object) {
396
+ result += `\n*... and ${fields.length - max_fields_per_object} more fields in this object*\n`;
397
+ }
398
+ result += '\n';
399
+ }
400
+
401
+ return {
402
+ content: [{
403
+ type: "text",
404
+ text: result
405
+ }]
406
+ };
407
+ }
408
+
409
+ async function handleGeneralOverview(documentation, options) {
410
+ const { show_custom_only, include_relationships, detailed_fields, include_permissions } = options;
411
+
412
+ let result = `📊 **Salesforce Installation - Complete Overview**\n\n`;
413
+
414
+ // Add user context if available and check for missing information
415
+ let contextWarning = '';
416
+ try {
417
+ const context = await getUserContext();
418
+
419
+ // Check if we have basic user information
420
+ const hasPersonalInfo = context.personal?.name && context.personal?.email;
421
+ const hasBusinessInfo = context.business?.company_name && context.business?.industry;
422
+ const hasDataModelInfo = Object.keys(context.data_model || {}).length > 0;
423
+
424
+ if (hasPersonalInfo || hasBusinessInfo) {
425
+ result = `📊 **Salesforce Installation - Complete Overview**\n`;
426
+ if (context.personal?.name) {
427
+ result += `*Kontext für: ${context.personal.name}`;
428
+ if (context.business?.company_name) {
429
+ result += ` (${context.business.company_name})`;
430
+ }
431
+ result += `*\n\n`;
432
+ }
433
+ }
434
+
435
+ // Generate warnings for missing context
436
+ const missingContext = [];
437
+ if (!hasPersonalInfo) {
438
+ missingContext.push("👤 **Persönliche Informationen**");
439
+ }
440
+ if (!hasBusinessInfo) {
441
+ missingContext.push("🏢 **Geschäftliche Informationen**");
442
+ }
443
+ if (!hasDataModelInfo && documentation.summary?.custom_objects > 0) {
444
+ missingContext.push("🗃️ **Datenmodell-Kontext**");
445
+ }
446
+
447
+ if (missingContext.length > 0) {
448
+ contextWarning = `\n## ⚠️ Fehlender Kontext für bessere Unterstützung\n\n`;
449
+ contextWarning += `Um Ihnen personalisierte und kontextbezogene Hilfe zu bieten, fehlen noch wichtige Informationen:\n\n`;
450
+ for (const missing of missingContext) {
451
+ contextWarning += `- ${missing}\n`;
452
+ }
453
+ contextWarning += `\n💡 **Empfehlung:** Starten Sie das Kontext-Interview:\n`;
454
+ contextWarning += `\`\`\`json\n`;
455
+ contextWarning += `{\n`;
456
+ contextWarning += ` "tool": "salesforce_learn_context",\n`;
457
+ contextWarning += ` "action": "start_interview"\n`;
458
+ contextWarning += `}\n`;
459
+ contextWarning += `\`\`\`\n\n`;
460
+
461
+ if (hasPersonalInfo && hasBusinessInfo && !hasDataModelInfo) {
462
+ contextWarning += `Oder für erweiterte Datenmodell-Fragen:\n`;
463
+ contextWarning += `\`\`\`json\n`;
464
+ contextWarning += `{\n`;
465
+ contextWarning += ` "tool": "salesforce_learn_context",\n`;
466
+ contextWarning += ` "action": "suggest_questions",\n`;
467
+ contextWarning += ` "context_type": "data_model"\n`;
468
+ contextWarning += `}\n`;
469
+ contextWarning += `\`\`\`\n\n`;
470
+ }
471
+ contextWarning += `---\n\n`;
472
+ }
473
+
474
+ } catch (error) {
475
+ // Context not available, suggest learning it
476
+ contextWarning = `\n## 💡 Personalisierung verfügbar\n\n`;
477
+ contextWarning += `Für bessere, personalisierte Unterstützung können Sie ein Kontext-Interview durchführen:\n\n`;
478
+ contextWarning += `\`\`\`json\n`;
479
+ contextWarning += `{\n`;
480
+ contextWarning += ` "tool": "salesforce_learn_context",\n`;
481
+ contextWarning += ` "action": "start_interview"\n`;
482
+ contextWarning += `}\n`;
483
+ contextWarning += `\`\`\`\n\n`;
484
+ contextWarning += `Dies hilft mir, Sie und Ihr Unternehmen kennenzulernen.\n\n`;
485
+ contextWarning += `---\n\n`;
486
+ }
487
+
488
+ // Add context warning if needed
489
+ result += contextWarning;
490
+
491
+ // Enhanced metadata info
492
+ result += `## 📋 Installation Metadata\n`;
493
+ result += `- **Last Learned:** ${new Date(documentation.metadata.learned_at).toLocaleString()}\n`;
494
+ if (documentation.metadata.salesforce_instance) {
495
+ result += `- **Salesforce Instance:** ${documentation.metadata.salesforce_instance}\n`;
496
+ }
497
+ result += `- **API Version:** ${documentation.metadata.api_version}\n`;
498
+ if (documentation.metadata.learning_options) {
499
+ result += `- **Learning Options:**\n`;
500
+ result += ` - Include Unused: ${documentation.metadata.learning_options.include_unused}\n`;
501
+ result += ` - Detailed Relationships: ${documentation.metadata.learning_options.detailed_relationships}\n`;
502
+ }
503
+
504
+ // Enhanced summary with more statistics
505
+ result += `\n## 📊 Installation Statistics\n`;
506
+ result += `- **Total Objects:** ${documentation.summary.total_objects}\n`;
507
+ result += `- **Standard Objects:** ${documentation.summary.standard_objects}\n`;
508
+ result += `- **Custom Objects:** ${documentation.summary.custom_objects}\n`;
509
+ result += `- **Total Fields:** ${documentation.summary.total_fields}\n`;
510
+ result += `- **Custom Fields:** ${documentation.summary.custom_fields}\n`;
511
+
512
+ // Calculate additional statistics
513
+ const objectsWithErrors = Object.values(documentation.objects).filter(obj => obj.error).length;
514
+ if (objectsWithErrors > 0) {
515
+ result += `- **Objects with Errors:** ${objectsWithErrors}\n`;
516
+ }
517
+
518
+ // Field type statistics
519
+ const fieldTypes = {};
520
+ let totalRelationships = 0;
521
+
522
+ for (const [objectName, obj] of Object.entries(documentation.objects)) {
523
+ if (obj.error) continue;
524
+
525
+ if (obj.relationships) {
526
+ totalRelationships += (obj.relationships.parent_relationships?.length || 0) +
527
+ (obj.relationships.child_relationships?.length || 0);
528
+ }
529
+
530
+ for (const [fieldName, field] of Object.entries(obj.fields || {})) {
531
+ const type = field.type || 'unknown';
532
+ fieldTypes[type] = (fieldTypes[type] || 0) + 1;
533
+ }
534
+ }
535
+
536
+ if (include_relationships) {
537
+ result += `- **Total Relationships:** ${totalRelationships}\n`;
538
+ }
539
+
540
+ // Field type breakdown
541
+ if (detailed_fields && Object.keys(fieldTypes).length > 0) {
542
+ result += `\n### Field Type Distribution\n`;
543
+ const sortedTypes = Object.entries(fieldTypes).sort(([,a], [,b]) => b - a);
544
+ for (const [type, count] of sortedTypes.slice(0, 10)) {
545
+ result += `- **${type}:** ${count} fields\n`;
546
+ }
547
+ if (sortedTypes.length > 10) {
548
+ result += `- *... and ${sortedTypes.length - 10} more field types*\n`;
549
+ }
550
+ }
551
+
552
+ // Complete object list with detailed information
553
+ const objects = Object.entries(documentation.objects)
554
+ .filter(([name, obj]) => !obj.error && (!show_custom_only || obj.basic_info.custom))
555
+ .sort(([a], [b]) => a.localeCompare(b));
556
+
557
+ if (objects.length > 0) {
558
+ result += `\n## 📦 ${show_custom_only ? 'Custom Objects' : 'All Objects'} (${objects.length})\n`;
559
+
560
+ for (const [objectName, obj] of objects) {
561
+ const custom = obj.basic_info.custom ? ' 🔧' : '';
562
+ const hasRelationships = obj.relationships &&
563
+ ((obj.relationships.parent_relationships?.length || 0) + (obj.relationships.child_relationships?.length || 0)) > 0;
564
+ const relationshipIndicator = include_relationships && hasRelationships ? ' 🔗' : '';
565
+
566
+ result += `\n### ${obj.basic_info.label} (\`${objectName}\`)${custom}${relationshipIndicator}\n`;
567
+ result += `- **Fields:** ${obj.field_count} total, ${obj.custom_field_count} custom\n`;
568
+
569
+ if (include_permissions && obj.metadata) {
570
+ const permissions = [];
571
+ if (obj.metadata.createable) permissions.push('Create');
572
+ if (obj.metadata.updateable) permissions.push('Update');
573
+ if (obj.metadata.deletable) permissions.push('Delete');
574
+ if (obj.metadata.queryable) permissions.push('Query');
575
+ result += `- **Permissions:** ${permissions.join(', ')}\n`;
576
+ }
577
+
578
+ if (include_relationships && obj.relationships) {
579
+ const parentCount = obj.relationships.parent_relationships?.length || 0;
580
+ const childCount = obj.relationships.child_relationships?.length || 0;
581
+ if (parentCount > 0 || childCount > 0) {
582
+ result += `- **Relationships:** ${parentCount} parent, ${childCount} child\n`;
583
+ }
584
+ }
585
+
586
+ // Show some important fields
587
+ if (detailed_fields && obj.fields) {
588
+ const importantFields = Object.entries(obj.fields)
589
+ .filter(([name, field]) => field.required || field.custom || ['Name', 'Email'].includes(name))
590
+ .slice(0, 3);
591
+
592
+ if (importantFields.length > 0) {
593
+ result += `- **Key Fields:** ${importantFields.map(([name, field]) => {
594
+ const indicators = [];
595
+ if (field.required) indicators.push('⚠️');
596
+ if (field.custom) indicators.push('🔧');
597
+ return `${field.label || name}${indicators.join('')}`;
598
+ }).join(', ')}\n`;
599
+ }
600
+ }
601
+ }
602
+ }
603
+
604
+ // Objects with errors
605
+ const errorObjects = Object.entries(documentation.objects)
606
+ .filter(([name, obj]) => obj.error);
607
+
608
+ if (errorObjects.length > 0) {
609
+ result += `\n## ⚠️ Objects with Analysis Errors (${errorObjects.length})\n`;
610
+ for (const [objectName, obj] of errorObjects.slice(0, 10)) {
611
+ result += `- **${objectName}:** ${obj.error}\n`;
612
+ }
613
+ if (errorObjects.length > 10) {
614
+ result += `- *... and ${errorObjects.length - 10} more objects with errors*\n`;
615
+ }
616
+ }
617
+
618
+ result += `\n## 💡 Usage Tips\n`;
619
+ result += `- **Specific Object:** Use \`object_name: "ObjectName"\` for complete object details\n`;
620
+ result += `- **Field Search:** Use \`field_search: "searchterm"\` to find specific fields\n`;
621
+ result += `- **Custom Only:** Use \`show_custom_only: true\` to focus on customizations\n`;
622
+ result += `- **Detailed Fields:** Use \`detailed_fields: true\` for complete field information\n`;
623
+ result += `- **Relationships:** Use \`include_relationships: true\` for relationship mapping\n`;
624
+ result += `- **Limit Results:** Use \`max_fields_per_object: N\` to limit field display\n`;
625
+
626
+ return {
627
+ content: [{
628
+ type: "text",
629
+ text: result
630
+ }]
631
+ };
632
+ }
633
+
634
+ function getWritabilityStatus(writability) {
635
+ if (!writability) return 'Unknown';
636
+
637
+ if (writability.read_only) {
638
+ if (writability.formula || writability.calculated) {
639
+ return '🔒 Read-Only (Formula/Calculated)';
640
+ } else if (writability.rollup_summary) {
641
+ return '🔒 Read-Only (Rollup Summary)';
642
+ } else if (writability.auto_number) {
643
+ return '🔒 Read-Only (Auto Number)';
644
+ } else if (writability.system_managed) {
645
+ return '🔒 Read-Only (System Managed)';
646
+ } else {
647
+ return '🔒 Read-Only';
648
+ }
649
+ } else if (writability.create_only) {
650
+ return '📝 Create Only';
651
+ } else if (writability.fully_writable) {
652
+ return '✅ Fully Writable';
653
+ } else {
654
+ return '⚠️ Limited Writability';
655
+ }
656
+ }