powerplatform-mcp 0.4.4 → 1.0.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.
package/build/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
- import { z } from "zod";
5
- import { PowerPlatformService } from "./PowerPlatformService.js";
4
+ import { PowerPlatformClient } from "./PowerPlatformClient.js";
5
+ import { EntityService, RecordService, OptionSetService, PluginService } from "./services/index.js";
6
+ import { registerAllTools } from "./tools/index.js";
7
+ import { registerAllPrompts } from "./prompts/index.js";
6
8
  // Environment configuration
7
- // These values can be set in environment variables or loaded from a configuration file
8
9
  const POWERPLATFORM_CONFIG = {
9
10
  organizationUrl: process.env.POWERPLATFORM_URL || "",
10
11
  clientId: process.env.POWERPLATFORM_CLIENT_ID || "",
@@ -16,10 +17,15 @@ const server = new McpServer({
16
17
  name: "powerplatform-mcp",
17
18
  version: "1.0.0",
18
19
  });
19
- let powerPlatformService = null;
20
- // Function to initialize PowerPlatformService on demand
21
- function getPowerPlatformService() {
22
- if (!powerPlatformService) {
20
+ // Service instances (lazy initialized)
21
+ let powerPlatformClient = null;
22
+ let entityService = null;
23
+ let recordService = null;
24
+ let optionSetService = null;
25
+ let pluginService = null;
26
+ // Function to initialize the PowerPlatform client on demand
27
+ function getClient() {
28
+ if (!powerPlatformClient) {
23
29
  // Check if configuration is complete
24
30
  const missingConfig = [];
25
31
  if (!POWERPLATFORM_CONFIG.organizationUrl)
@@ -33,637 +39,46 @@ function getPowerPlatformService() {
33
39
  if (missingConfig.length > 0) {
34
40
  throw new Error(`Missing PowerPlatform configuration: ${missingConfig.join(", ")}. Set these in environment variables.`);
35
41
  }
36
- // Initialize service
37
- powerPlatformService = new PowerPlatformService(POWERPLATFORM_CONFIG);
38
- console.error("PowerPlatform service initialized");
42
+ powerPlatformClient = new PowerPlatformClient(POWERPLATFORM_CONFIG);
43
+ console.error("PowerPlatform client initialized");
39
44
  }
40
- return powerPlatformService;
45
+ return powerPlatformClient;
41
46
  }
42
- // Pre-defined PowerPlatform Prompts
43
- const powerPlatformPrompts = {
44
- // Entity exploration prompts
45
- ENTITY_OVERVIEW: (entityName) => `## Power Platform Entity: ${entityName}\n\n` +
46
- `This is an overview of the '${entityName}' entity in Microsoft Power Platform/Dataverse:\n\n` +
47
- `### Entity Details\n{{entity_details}}\n\n` +
48
- `### Attributes\n{{key_attributes}}\n\n` +
49
- `### Relationships\n{{relationships}}\n\n` +
50
- `You can query this entity using OData filters against the plural name.`,
51
- ATTRIBUTE_DETAILS: (entityName, attributeName) => `## Attribute: ${attributeName}\n\n` +
52
- `Details for the '${attributeName}' attribute of the '${entityName}' entity:\n\n` +
53
- `{{attribute_details}}\n\n` +
54
- `### Usage Notes\n` +
55
- `- Data Type: {{data_type}}\n` +
56
- `- Required: {{required}}\n` +
57
- `- Max Length: {{max_length}}`,
58
- // Query builder prompts
59
- QUERY_TEMPLATE: (entityNamePlural) => `## OData Query Template for ${entityNamePlural}\n\n` +
60
- `Use this template to build queries against the ${entityNamePlural} entity:\n\n` +
61
- `\`\`\`\n${entityNamePlural}?$select={{selected_fields}}&$filter={{filter_conditions}}&$orderby={{order_by}}&$top={{max_records}}\n\`\`\`\n\n` +
62
- `### Common Filter Examples\n` +
63
- `- Equals: \`name eq 'Contoso'\`\n` +
64
- `- Contains: \`contains(name, 'Contoso')\`\n` +
65
- `- Greater than date: \`createdon gt 2023-01-01T00:00:00Z\`\n` +
66
- `- Multiple conditions: \`name eq 'Contoso' and statecode eq 0\``,
67
- // Relationship exploration prompts
68
- RELATIONSHIP_MAP: (entityName) => `## Relationship Map for ${entityName}\n\n` +
69
- `This shows all relationships for the '${entityName}' entity:\n\n` +
70
- `### One-to-Many Relationships (${entityName} as Primary)\n{{one_to_many_primary}}\n\n` +
71
- `### One-to-Many Relationships (${entityName} as Related)\n{{one_to_many_related}}\n\n` +
72
- `### Many-to-Many Relationships\n{{many_to_many}}\n\n`
73
- };
74
- // Register prompts with the server using the correct method signature
75
- // Entity Overview Prompt
76
- server.prompt("entity-overview", "Get an overview of a Power Platform entity", {
77
- entityName: z.string().describe("The logical name of the entity")
78
- }, async (args) => {
79
- try {
80
- const service = getPowerPlatformService();
81
- const entityName = args.entityName;
82
- // Get entity metadata and key attributes
83
- const [metadata, attributes] = await Promise.all([
84
- service.getEntityMetadata(entityName),
85
- service.getEntityAttributes(entityName)
86
- ]);
87
- // Format entity details
88
- const entityDetails = `- Display Name: ${metadata.DisplayName?.UserLocalizedLabel?.Label || entityName}\n` +
89
- `- Schema Name: ${metadata.SchemaName}\n` +
90
- `- Description: ${metadata.Description?.UserLocalizedLabel?.Label || 'No description'}\n` +
91
- `- Primary Key: ${metadata.PrimaryIdAttribute}\n` +
92
- `- Primary Name: ${metadata.PrimaryNameAttribute}`;
93
- // Get key attributes
94
- const keyAttributes = attributes.value
95
- .map((attr) => {
96
- const attrType = attr["@odata.type"] || attr.odata?.type || "Unknown type";
97
- return `- ${attr.LogicalName}: ${attrType}`;
98
- })
99
- .join('\n');
100
- // Get relationships summary
101
- const relationships = await service.getEntityRelationships(entityName);
102
- const oneToManyCount = relationships.oneToMany.value.length;
103
- const manyToManyCount = relationships.manyToMany.value.length;
104
- const relationshipsSummary = `- One-to-Many Relationships: ${oneToManyCount}\n` +
105
- `- Many-to-Many Relationships: ${manyToManyCount}`;
106
- let promptContent = powerPlatformPrompts.ENTITY_OVERVIEW(entityName);
107
- promptContent = promptContent
108
- .replace('{{entity_details}}', entityDetails)
109
- .replace('{{key_attributes}}', keyAttributes)
110
- .replace('{{relationships}}', relationshipsSummary);
111
- return {
112
- messages: [
113
- {
114
- role: "assistant",
115
- content: {
116
- type: "text",
117
- text: promptContent
118
- }
119
- }
120
- ]
121
- };
122
- }
123
- catch (error) {
124
- console.error(`Error handling entity-overview prompt:`, error);
125
- return {
126
- messages: [
127
- {
128
- role: "assistant",
129
- content: {
130
- type: "text",
131
- text: `Error: ${error.message}`
132
- }
133
- }
134
- ]
135
- };
136
- }
137
- });
138
- // Attribute Details Prompt
139
- server.prompt("attribute-details", "Get detailed information about a specific entity attribute/field", {
140
- entityName: z.string().describe("The logical name of the entity"),
141
- attributeName: z.string().describe("The logical name of the attribute"),
142
- }, async (args) => {
143
- try {
144
- const service = getPowerPlatformService();
145
- const { entityName, attributeName } = args;
146
- // Get attribute details
147
- const attribute = await service.getEntityAttribute(entityName, attributeName);
148
- // Format attribute details
149
- const attrDetails = `- Display Name: ${attribute.DisplayName?.UserLocalizedLabel?.Label || attributeName}\n` +
150
- `- Description: ${attribute.Description?.UserLocalizedLabel?.Label || 'No description'}\n` +
151
- `- Type: ${attribute.AttributeType}\n` +
152
- `- Format: ${attribute.Format || 'N/A'}\n` +
153
- `- Is Required: ${attribute.RequiredLevel?.Value || 'No'}\n` +
154
- `- Is Searchable: ${attribute.IsValidForAdvancedFind || false}`;
155
- let promptContent = powerPlatformPrompts.ATTRIBUTE_DETAILS(entityName, attributeName);
156
- promptContent = promptContent
157
- .replace('{{attribute_details}}', attrDetails)
158
- .replace('{{data_type}}', attribute.AttributeType)
159
- .replace('{{required}}', attribute.RequiredLevel?.Value || 'No')
160
- .replace('{{max_length}}', attribute.MaxLength || 'N/A');
161
- return {
162
- messages: [
163
- {
164
- role: "assistant",
165
- content: {
166
- type: "text",
167
- text: promptContent
168
- }
169
- }
170
- ]
171
- };
172
- }
173
- catch (error) {
174
- console.error(`Error handling attribute-details prompt:`, error);
175
- return {
176
- messages: [
177
- {
178
- role: "assistant",
179
- content: {
180
- type: "text",
181
- text: `Error: ${error.message}`
182
- }
183
- }
184
- ]
185
- };
186
- }
187
- });
188
- // Query Template Prompt
189
- server.prompt("query-template", "Get a template for querying a Power Platform entity", {
190
- entityName: z.string().describe("The logical name of the entity"),
191
- }, async (args) => {
192
- try {
193
- const service = getPowerPlatformService();
194
- const entityName = args.entityName;
195
- // Get entity metadata to determine plural name
196
- const metadata = await service.getEntityMetadata(entityName);
197
- const entityNamePlural = metadata.EntitySetName;
198
- // Get a few important fields for the select example
199
- const attributes = await service.getEntityAttributes(entityName);
200
- const selectFields = attributes.value
201
- .filter((attr) => attr.IsValidForRead === true && !attr.AttributeOf)
202
- .slice(0, 5) // Just take first 5 for example
203
- .map((attr) => attr.LogicalName)
204
- .join(',');
205
- let promptContent = powerPlatformPrompts.QUERY_TEMPLATE(entityNamePlural);
206
- promptContent = promptContent
207
- .replace('{{selected_fields}}', selectFields)
208
- .replace('{{filter_conditions}}', `${metadata.PrimaryNameAttribute} eq 'Example'`)
209
- .replace('{{order_by}}', `${metadata.PrimaryNameAttribute} asc`)
210
- .replace('{{max_records}}', '50');
211
- return {
212
- messages: [
213
- {
214
- role: "assistant",
215
- content: {
216
- type: "text",
217
- text: promptContent
218
- }
219
- }
220
- ]
221
- };
222
- }
223
- catch (error) {
224
- console.error(`Error handling query-template prompt:`, error);
225
- return {
226
- messages: [
227
- {
228
- role: "assistant",
229
- content: {
230
- type: "text",
231
- text: `Error: ${error.message}`
232
- }
233
- }
234
- ]
235
- };
236
- }
237
- });
238
- // Relationship Map Prompt
239
- server.prompt("relationship-map", "Get a list of relationships for a Power Platform entity", {
240
- entityName: z.string().describe("The logical name of the entity"),
241
- }, async (args) => {
242
- try {
243
- const service = getPowerPlatformService();
244
- const entityName = args.entityName;
245
- // Get relationships
246
- const relationships = await service.getEntityRelationships(entityName);
247
- // Format one-to-many relationships where this entity is primary
248
- const oneToManyPrimary = relationships.oneToMany.value
249
- .filter((rel) => rel.ReferencingEntity !== entityName)
250
- .map((rel) => `- ${rel.SchemaName}: ${entityName} (1) → ${rel.ReferencingEntity} (N)`)
251
- .join('\n');
252
- // Format one-to-many relationships where this entity is related
253
- const oneToManyRelated = relationships.oneToMany.value
254
- .filter((rel) => rel.ReferencingEntity === entityName)
255
- .map((rel) => `- ${rel.SchemaName}: ${rel.ReferencedEntity} (1) → ${entityName} (N)`)
256
- .join('\n');
257
- // Format many-to-many relationships
258
- const manyToMany = relationships.manyToMany.value
259
- .map((rel) => {
260
- const otherEntity = rel.Entity1LogicalName === entityName ? rel.Entity2LogicalName : rel.Entity1LogicalName;
261
- return `- ${rel.SchemaName}: ${entityName} (N) ↔ ${otherEntity} (N)`;
262
- })
263
- .join('\n');
264
- let promptContent = powerPlatformPrompts.RELATIONSHIP_MAP(entityName);
265
- promptContent = promptContent
266
- .replace('{{one_to_many_primary}}', oneToManyPrimary || 'None found')
267
- .replace('{{one_to_many_related}}', oneToManyRelated || 'None found')
268
- .replace('{{many_to_many}}', manyToMany || 'None found');
269
- return {
270
- messages: [
271
- {
272
- role: "assistant",
273
- content: {
274
- type: "text",
275
- text: promptContent
276
- }
277
- }
278
- ]
279
- };
280
- }
281
- catch (error) {
282
- console.error(`Error handling relationship-map prompt:`, error);
283
- return {
284
- messages: [
285
- {
286
- role: "assistant",
287
- content: {
288
- type: "text",
289
- text: `Error: ${error.message}`
290
- }
291
- }
292
- ]
293
- };
294
- }
295
- });
296
- // PowerPlatform entity metadata
297
- server.tool("get-entity-metadata", "Get metadata about a PowerPlatform entity", {
298
- entityName: z.string().describe("The logical name of the entity"),
299
- }, async ({ entityName }) => {
300
- try {
301
- // Get or initialize PowerPlatformService
302
- const service = getPowerPlatformService();
303
- const metadata = await service.getEntityMetadata(entityName);
304
- // Format the metadata as a string for text display
305
- const metadataStr = JSON.stringify(metadata, null, 2);
306
- return {
307
- content: [
308
- {
309
- type: "text",
310
- text: `Entity metadata for '${entityName}':\n\n${metadataStr}`,
311
- },
312
- ],
313
- };
314
- }
315
- catch (error) {
316
- console.error("Error getting entity metadata:", error);
317
- return {
318
- content: [
319
- {
320
- type: "text",
321
- text: `Failed to get entity metadata: ${error.message}`,
322
- },
323
- ],
324
- };
325
- }
326
- });
327
- // PowerPlatform entity attributes
328
- server.tool("get-entity-attributes", "Get attributes/fields of a PowerPlatform entity", {
329
- entityName: z.string().describe("The logical name of the entity"),
330
- }, async ({ entityName }) => {
331
- try {
332
- // Get or initialize PowerPlatformService
333
- const service = getPowerPlatformService();
334
- const attributes = await service.getEntityAttributes(entityName);
335
- // Format the attributes as a string for text display
336
- const attributesStr = JSON.stringify(attributes, null, 2);
337
- return {
338
- content: [
339
- {
340
- type: "text",
341
- text: `Attributes for entity '${entityName}':\n\n${attributesStr}`,
342
- },
343
- ],
344
- };
345
- }
346
- catch (error) {
347
- console.error("Error getting entity attributes:", error);
348
- return {
349
- content: [
350
- {
351
- type: "text",
352
- text: `Failed to get entity attributes: ${error.message}`,
353
- },
354
- ],
355
- };
356
- }
357
- });
358
- // PowerPlatform specific entity attribute
359
- server.tool("get-entity-attribute", "Get a specific attribute/field of a PowerPlatform entity", {
360
- entityName: z.string().describe("The logical name of the entity"),
361
- attributeName: z.string().describe("The logical name of the attribute")
362
- }, async ({ entityName, attributeName }) => {
363
- try {
364
- // Get or initialize PowerPlatformService
365
- const service = getPowerPlatformService();
366
- const attribute = await service.getEntityAttribute(entityName, attributeName);
367
- // Format the attribute as a string for text display
368
- const attributeStr = JSON.stringify(attribute, null, 2);
369
- return {
370
- content: [
371
- {
372
- type: "text",
373
- text: `Attribute '${attributeName}' for entity '${entityName}':\n\n${attributeStr}`,
374
- },
375
- ],
376
- };
377
- }
378
- catch (error) {
379
- console.error("Error getting entity attribute:", error);
380
- return {
381
- content: [
382
- {
383
- type: "text",
384
- text: `Failed to get entity attribute: ${error.message}`,
385
- },
386
- ],
387
- };
47
+ // Service getters with lazy initialization
48
+ function getEntityService() {
49
+ if (!entityService) {
50
+ entityService = new EntityService(getClient());
388
51
  }
389
- });
390
- // PowerPlatform entity relationships
391
- server.tool("get-entity-relationships", "Get relationships (one-to-many and many-to-many) for a PowerPlatform entity", {
392
- entityName: z.string().describe("The logical name of the entity"),
393
- }, async ({ entityName }) => {
394
- try {
395
- // Get or initialize PowerPlatformService
396
- const service = getPowerPlatformService();
397
- const relationships = await service.getEntityRelationships(entityName);
398
- // Format the relationships as a string for text display
399
- const relationshipsStr = JSON.stringify(relationships, null, 2);
400
- return {
401
- content: [
402
- {
403
- type: "text",
404
- text: `Relationships for entity '${entityName}':\n\n${relationshipsStr}`,
405
- },
406
- ],
407
- };
408
- }
409
- catch (error) {
410
- console.error("Error getting entity relationships:", error);
411
- return {
412
- content: [
413
- {
414
- type: "text",
415
- text: `Failed to get entity relationships: ${error.message}`,
416
- },
417
- ],
418
- };
419
- }
420
- });
421
- // PowerPlatform global option set
422
- server.tool("get-global-option-set", "Get a global option set definition by name", {
423
- optionSetName: z.string().describe("The name of the global option set"),
424
- }, async ({ optionSetName }) => {
425
- try {
426
- // Get or initialize PowerPlatformService
427
- const service = getPowerPlatformService();
428
- const optionSet = await service.getGlobalOptionSet(optionSetName);
429
- // Format the option set as a string for text display
430
- const optionSetStr = JSON.stringify(optionSet, null, 2);
431
- return {
432
- content: [
433
- {
434
- type: "text",
435
- text: `Global option set '${optionSetName}':\n\n${optionSetStr}`,
436
- },
437
- ],
438
- };
439
- }
440
- catch (error) {
441
- console.error("Error getting global option set:", error);
442
- return {
443
- content: [
444
- {
445
- type: "text",
446
- text: `Failed to get global option set: ${error.message}`,
447
- },
448
- ],
449
- };
450
- }
451
- });
452
- // PowerPlatform record by ID
453
- server.tool("get-record", "Get a specific record by entity name (plural) and ID", {
454
- entityNamePlural: z.string().describe("The plural name of the entity (e.g., 'accounts', 'contacts')"),
455
- recordId: z.string().describe("The GUID of the record"),
456
- }, async ({ entityNamePlural, recordId }) => {
457
- try {
458
- // Get or initialize PowerPlatformService
459
- const service = getPowerPlatformService();
460
- const record = await service.getRecord(entityNamePlural, recordId);
461
- // Format the record as a string for text display
462
- const recordStr = JSON.stringify(record, null, 2);
463
- return {
464
- content: [
465
- {
466
- type: "text",
467
- text: `Record from '${entityNamePlural}' with ID '${recordId}':\n\n${recordStr}`,
468
- },
469
- ],
470
- };
471
- }
472
- catch (error) {
473
- console.error("Error getting record:", error);
474
- return {
475
- content: [
476
- {
477
- type: "text",
478
- text: `Failed to get record: ${error.message}`,
479
- },
480
- ],
481
- };
482
- }
483
- });
484
- // PowerPlatform query records with filter
485
- server.tool("query-records", "Query records using an OData filter expression", {
486
- entityNamePlural: z.string().describe("The plural name of the entity (e.g., 'accounts', 'contacts')"),
487
- filter: z.string().describe("OData filter expression (e.g., \"name eq 'test'\" or \"createdon gt 2023-01-01\")"),
488
- maxRecords: z.number().optional().describe("Maximum number of records to retrieve (default: 50)"),
489
- }, async ({ entityNamePlural, filter, maxRecords }) => {
490
- try {
491
- // Get or initialize PowerPlatformService
492
- const service = getPowerPlatformService();
493
- const records = await service.queryRecords(entityNamePlural, filter, maxRecords || 50);
494
- // Format the records as a string for text display
495
- const recordsStr = JSON.stringify(records, null, 2);
496
- const recordCount = records.value?.length || 0;
497
- return {
498
- content: [
499
- {
500
- type: "text",
501
- text: `Retrieved ${recordCount} records from '${entityNamePlural}' with filter '${filter}':\n\n${recordsStr}`,
502
- },
503
- ],
504
- };
505
- }
506
- catch (error) {
507
- console.error("Error querying records:", error);
508
- return {
509
- content: [
510
- {
511
- type: "text",
512
- text: `Failed to query records: ${error.message}`,
513
- },
514
- ],
515
- };
52
+ return entityService;
53
+ }
54
+ function getRecordService() {
55
+ if (!recordService) {
56
+ recordService = new RecordService(getClient());
516
57
  }
517
- });
518
- // PowerPlatform MCP Prompts
519
- server.tool("use-powerplatform-prompt", "Use a predefined prompt template for PowerPlatform entities", {
520
- promptType: z.enum([
521
- "ENTITY_OVERVIEW",
522
- "ATTRIBUTE_DETAILS",
523
- "QUERY_TEMPLATE",
524
- "RELATIONSHIP_MAP"
525
- ]).describe("The type of prompt template to use"),
526
- entityName: z.string().describe("The logical name of the entity"),
527
- attributeName: z.string().optional().describe("The logical name of the attribute (required for ATTRIBUTE_DETAILS prompt)"),
528
- }, async ({ promptType, entityName, attributeName }) => {
529
- try {
530
- // Get or initialize PowerPlatformService
531
- const service = getPowerPlatformService();
532
- let promptContent = "";
533
- let replacements = {};
534
- switch (promptType) {
535
- case "ENTITY_OVERVIEW": {
536
- // Get entity metadata and key attributes
537
- const [metadata, attributes] = await Promise.all([
538
- service.getEntityMetadata(entityName),
539
- service.getEntityAttributes(entityName)
540
- ]);
541
- // Format entity details
542
- const entityDetails = `- Display Name: ${metadata.DisplayName?.UserLocalizedLabel?.Label || entityName}\n` +
543
- `- Schema Name: ${metadata.SchemaName}\n` +
544
- `- Description: ${metadata.Description?.UserLocalizedLabel?.Label || 'No description'}\n` +
545
- `- Primary Key: ${metadata.PrimaryIdAttribute}\n` +
546
- `- Primary Name: ${metadata.PrimaryNameAttribute}`;
547
- // Get key attributes
548
- const keyAttributes = attributes.value
549
- //.slice(0, 10) // Limit to first 10 important attributes
550
- .map((attr) => {
551
- const attrType = attr["@odata.type"] || attr.odata?.type || "Unknown type";
552
- return `- ${attr.LogicalName}: ${attrType}`;
553
- })
554
- .join('\n');
555
- // Get relationships summary
556
- const relationships = await service.getEntityRelationships(entityName);
557
- const oneToManyCount = relationships.oneToMany.value.length;
558
- const manyToManyCount = relationships.manyToMany.value.length;
559
- const relationshipsSummary = `- One-to-Many Relationships: ${oneToManyCount}\n` +
560
- `- Many-to-Many Relationships: ${manyToManyCount}`;
561
- promptContent = powerPlatformPrompts.ENTITY_OVERVIEW(entityName);
562
- replacements = {
563
- '{{entity_details}}': entityDetails,
564
- '{{key_attributes}}': keyAttributes,
565
- '{{relationships}}': relationshipsSummary
566
- };
567
- break;
568
- }
569
- case "ATTRIBUTE_DETAILS": {
570
- if (!attributeName) {
571
- throw new Error("attributeName is required for ATTRIBUTE_DETAILS prompt");
572
- }
573
- // Get attribute details
574
- const attribute = await service.getEntityAttribute(entityName, attributeName);
575
- // Format attribute details
576
- const attrDetails = `- Display Name: ${attribute.DisplayName?.UserLocalizedLabel?.Label || attributeName}\n` +
577
- `- Description: ${attribute.Description?.UserLocalizedLabel?.Label || 'No description'}\n` +
578
- `- Type: ${attribute.AttributeType}\n` +
579
- `- Format: ${attribute.Format || 'N/A'}\n` +
580
- `- Is Required: ${attribute.RequiredLevel?.Value || 'No'}\n` +
581
- `- Is Searchable: ${attribute.IsValidForAdvancedFind || false}`;
582
- promptContent = powerPlatformPrompts.ATTRIBUTE_DETAILS(entityName, attributeName);
583
- replacements = {
584
- '{{attribute_details}}': attrDetails,
585
- '{{data_type}}': attribute.AttributeType,
586
- '{{required}}': attribute.RequiredLevel?.Value || 'No',
587
- '{{max_length}}': attribute.MaxLength || 'N/A'
588
- };
589
- break;
590
- }
591
- case "QUERY_TEMPLATE": {
592
- // Get entity metadata to determine plural name
593
- const metadata = await service.getEntityMetadata(entityName);
594
- const entityNamePlural = metadata.EntitySetName;
595
- // Get a few important fields for the select example
596
- const attributes = await service.getEntityAttributes(entityName);
597
- const selectFields = attributes.value
598
- .slice(0, 5) // Just take first 5 for example
599
- .map((attr) => attr.LogicalName)
600
- .join(',');
601
- promptContent = powerPlatformPrompts.QUERY_TEMPLATE(entityNamePlural);
602
- replacements = {
603
- '{{selected_fields}}': selectFields,
604
- '{{filter_conditions}}': `${metadata.PrimaryNameAttribute} eq 'Example'`,
605
- '{{order_by}}': `${metadata.PrimaryNameAttribute} asc`,
606
- '{{max_records}}': '50'
607
- };
608
- break;
609
- }
610
- case "RELATIONSHIP_MAP": {
611
- // Get relationships
612
- const relationships = await service.getEntityRelationships(entityName);
613
- // Format one-to-many relationships where this entity is primary
614
- const oneToManyPrimary = relationships.oneToMany.value
615
- .filter((rel) => rel.ReferencingEntity !== entityName)
616
- //.slice(0, 10) // Limit to 10 for readability
617
- .map((rel) => `- ${rel.SchemaName}: ${entityName} (1) → ${rel.ReferencingEntity} (N)`)
618
- .join('\n');
619
- // Format one-to-many relationships where this entity is related
620
- const oneToManyRelated = relationships.oneToMany.value
621
- .filter((rel) => rel.ReferencingEntity === entityName)
622
- //.slice(0, 10) // Limit to 10 for readability
623
- .map((rel) => `- ${rel.SchemaName}: ${rel.ReferencedEntity} (1) → ${entityName} (N)`)
624
- .join('\n');
625
- // Format many-to-many relationships
626
- const manyToMany = relationships.manyToMany.value
627
- //.slice(0, 10) // Limit to 10 for readability
628
- .map((rel) => {
629
- const otherEntity = rel.Entity1LogicalName === entityName ? rel.Entity2LogicalName : rel.Entity1LogicalName;
630
- return `- ${rel.SchemaName}: ${entityName} (N) ↔ ${otherEntity} (N)`;
631
- })
632
- .join('\n');
633
- promptContent = powerPlatformPrompts.RELATIONSHIP_MAP(entityName);
634
- replacements = {
635
- '{{one_to_many_primary}}': oneToManyPrimary || 'None found',
636
- '{{one_to_many_related}}': oneToManyRelated || 'None found',
637
- '{{many_to_many}}': manyToMany || 'None found'
638
- };
639
- break;
640
- }
641
- }
642
- // Replace all placeholders in the template
643
- for (const [placeholder, value] of Object.entries(replacements)) {
644
- promptContent = promptContent.replace(placeholder, value);
645
- }
646
- return {
647
- content: [
648
- {
649
- type: "text",
650
- text: promptContent,
651
- },
652
- ],
653
- };
58
+ return recordService;
59
+ }
60
+ function getOptionSetService() {
61
+ if (!optionSetService) {
62
+ optionSetService = new OptionSetService(getClient());
654
63
  }
655
- catch (error) {
656
- console.error("Error using PowerPlatform prompt:", error);
657
- return {
658
- content: [
659
- {
660
- type: "text",
661
- text: `Failed to use PowerPlatform prompt: ${error.message}`,
662
- },
663
- ],
664
- };
64
+ return optionSetService;
65
+ }
66
+ function getPluginService() {
67
+ if (!pluginService) {
68
+ pluginService = new PluginService(getClient());
665
69
  }
666
- });
70
+ return pluginService;
71
+ }
72
+ // Create service context for tools and prompts
73
+ const ctx = {
74
+ getEntityService,
75
+ getRecordService,
76
+ getOptionSetService,
77
+ getPluginService,
78
+ };
79
+ // Register all tools and prompts
80
+ registerAllTools(server, ctx);
81
+ registerAllPrompts(server, ctx);
667
82
  async function main() {
668
83
  const transport = new StdioServerTransport();
669
84
  await server.connect(transport);