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