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/README.md +38 -49
- package/build/PowerPlatformClient.js +83 -0
- package/build/PowerPlatformService.js +2 -32
- package/build/index.js +46 -657
- package/build/models/ApiCollectionResponse.js +1 -0
- package/build/models/index.js +1 -0
- package/build/prompts/entityPrompts.js +244 -0
- package/build/prompts/index.js +9 -0
- package/build/prompts/templates.js +34 -0
- package/build/services/EntityService.js +128 -0
- package/build/services/OptionSetService.js +18 -0
- package/build/services/PluginService.js +288 -0
- package/build/services/RecordService.js +29 -0
- package/build/services/index.js +4 -0
- package/build/tools/entityTools.js +156 -0
- package/build/tools/index.js +17 -0
- package/build/tools/optionSetTools.js +43 -0
- package/build/tools/pluginTools.js +188 -0
- package/build/tools/recordTools.js +87 -0
- package/build/types.js +1 -0
- package/package.json +12 -7
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Register plugin tools with the MCP server.
|
|
4
|
+
*/
|
|
5
|
+
export function registerPluginTools(server, ctx) {
|
|
6
|
+
// Get Plugin Assemblies
|
|
7
|
+
server.registerTool("get-plugin-assemblies", {
|
|
8
|
+
title: "Get Plugin Assemblies",
|
|
9
|
+
description: "Get all plugin assemblies in the environment",
|
|
10
|
+
inputSchema: {
|
|
11
|
+
includeManaged: z.boolean().optional().describe("Include managed assemblies (default: false)"),
|
|
12
|
+
maxRecords: z.number().optional().describe("Maximum number of records to retrieve (default: 100)"),
|
|
13
|
+
},
|
|
14
|
+
outputSchema: z.object({
|
|
15
|
+
totalCount: z.number(),
|
|
16
|
+
assemblies: z.any(),
|
|
17
|
+
}),
|
|
18
|
+
}, async ({ includeManaged, maxRecords }) => {
|
|
19
|
+
try {
|
|
20
|
+
const service = ctx.getPluginService();
|
|
21
|
+
const result = await service.getPluginAssemblies(includeManaged ?? false, maxRecords ?? 100);
|
|
22
|
+
return {
|
|
23
|
+
structuredContent: result,
|
|
24
|
+
content: [
|
|
25
|
+
{
|
|
26
|
+
type: "text",
|
|
27
|
+
text: `Found ${result.totalCount} plugin assemblies:\n\n${JSON.stringify(result.assemblies, null, 2)}`,
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
console.error("Error getting plugin assemblies:", error);
|
|
34
|
+
return {
|
|
35
|
+
content: [
|
|
36
|
+
{
|
|
37
|
+
type: "text",
|
|
38
|
+
text: `Failed to get plugin assemblies: ${error.message}`,
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
// Get Plugin Assembly Complete
|
|
45
|
+
server.registerTool("get-plugin-assembly-complete", {
|
|
46
|
+
title: "Get Plugin Assembly Complete",
|
|
47
|
+
description: "Get a plugin assembly by name with all related plugin types, steps, and images",
|
|
48
|
+
inputSchema: {
|
|
49
|
+
assemblyName: z.string().describe("The name of the plugin assembly"),
|
|
50
|
+
includeDisabled: z.boolean().optional().describe("Include disabled steps (default: false)"),
|
|
51
|
+
},
|
|
52
|
+
outputSchema: z.object({
|
|
53
|
+
assembly: z.any(),
|
|
54
|
+
pluginTypes: z.any(),
|
|
55
|
+
steps: z.any(),
|
|
56
|
+
validation: z.any(),
|
|
57
|
+
}),
|
|
58
|
+
}, async ({ assemblyName, includeDisabled }) => {
|
|
59
|
+
try {
|
|
60
|
+
const service = ctx.getPluginService();
|
|
61
|
+
const result = await service.getPluginAssemblyComplete(assemblyName, includeDisabled ?? false);
|
|
62
|
+
return {
|
|
63
|
+
structuredContent: result,
|
|
64
|
+
content: [
|
|
65
|
+
{
|
|
66
|
+
type: "text",
|
|
67
|
+
text: `Plugin assembly '${assemblyName}':\n\n` +
|
|
68
|
+
`Plugin Types: ${result.pluginTypes.length}\n` +
|
|
69
|
+
`Steps: ${result.steps.length}\n` +
|
|
70
|
+
`Potential Issues: ${result.validation.potentialIssues.length > 0 ? result.validation.potentialIssues.join(', ') : 'None'}\n\n` +
|
|
71
|
+
`${JSON.stringify(result, null, 2)}`,
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
console.error("Error getting plugin assembly:", error);
|
|
78
|
+
return {
|
|
79
|
+
content: [
|
|
80
|
+
{
|
|
81
|
+
type: "text",
|
|
82
|
+
text: `Failed to get plugin assembly: ${error.message}`,
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
// Get Entity Plugin Pipeline
|
|
89
|
+
server.registerTool("get-entity-plugin-pipeline", {
|
|
90
|
+
title: "Get Entity Plugin Pipeline",
|
|
91
|
+
description: "Get all plugins that execute on a specific entity, organized by message and stage",
|
|
92
|
+
inputSchema: {
|
|
93
|
+
entityName: z.string().describe("The logical name of the entity"),
|
|
94
|
+
messageFilter: z.string().optional().describe("Filter by specific message (e.g., 'Create', 'Update', 'Delete')"),
|
|
95
|
+
includeDisabled: z.boolean().optional().describe("Include disabled steps (default: false)"),
|
|
96
|
+
},
|
|
97
|
+
outputSchema: z.object({
|
|
98
|
+
entity: z.string(),
|
|
99
|
+
messages: z.any(),
|
|
100
|
+
steps: z.any(),
|
|
101
|
+
executionOrder: z.any(),
|
|
102
|
+
}),
|
|
103
|
+
}, async ({ entityName, messageFilter, includeDisabled }) => {
|
|
104
|
+
try {
|
|
105
|
+
const service = ctx.getPluginService();
|
|
106
|
+
const result = await service.getEntityPluginPipeline(entityName, messageFilter, includeDisabled ?? false);
|
|
107
|
+
return {
|
|
108
|
+
structuredContent: result,
|
|
109
|
+
content: [
|
|
110
|
+
{
|
|
111
|
+
type: "text",
|
|
112
|
+
text: `Plugin pipeline for entity '${entityName}':\n\n` +
|
|
113
|
+
`Total Steps: ${result.steps.length}\n` +
|
|
114
|
+
`Messages: ${result.messages.length}\n\n` +
|
|
115
|
+
`Execution Order:\n${result.executionOrder.map((name, i) => `${i + 1}. ${name}`).join('\n')}\n\n` +
|
|
116
|
+
`${JSON.stringify(result, null, 2)}`,
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
console.error("Error getting entity plugin pipeline:", error);
|
|
123
|
+
return {
|
|
124
|
+
content: [
|
|
125
|
+
{
|
|
126
|
+
type: "text",
|
|
127
|
+
text: `Failed to get entity plugin pipeline: ${error.message}`,
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
// Get Plugin Trace Logs
|
|
134
|
+
server.registerTool("get-plugin-trace-logs", {
|
|
135
|
+
title: "Get Plugin Trace Logs",
|
|
136
|
+
description: "Get plugin trace logs with filtering options",
|
|
137
|
+
inputSchema: {
|
|
138
|
+
entityName: z.string().optional().describe("Filter by entity name"),
|
|
139
|
+
messageName: z.string().optional().describe("Filter by message name (e.g., 'Create', 'Update')"),
|
|
140
|
+
correlationId: z.string().optional().describe("Filter by correlation ID"),
|
|
141
|
+
pluginStepId: z.string().optional().describe("Filter by plugin step ID"),
|
|
142
|
+
exceptionOnly: z.boolean().optional().describe("Only show logs with exceptions (default: false)"),
|
|
143
|
+
hoursBack: z.number().optional().describe("Hours to look back (default: 24)"),
|
|
144
|
+
maxRecords: z.number().optional().describe("Maximum number of records (default: 50)"),
|
|
145
|
+
},
|
|
146
|
+
outputSchema: z.object({
|
|
147
|
+
totalCount: z.number(),
|
|
148
|
+
logs: z.any(),
|
|
149
|
+
}),
|
|
150
|
+
}, async ({ entityName, messageName, correlationId, pluginStepId, exceptionOnly, hoursBack, maxRecords }) => {
|
|
151
|
+
try {
|
|
152
|
+
const service = ctx.getPluginService();
|
|
153
|
+
const result = await service.getPluginTraceLogs({
|
|
154
|
+
entityName,
|
|
155
|
+
messageName,
|
|
156
|
+
correlationId,
|
|
157
|
+
pluginStepId,
|
|
158
|
+
exceptionOnly: exceptionOnly ?? false,
|
|
159
|
+
hoursBack: hoursBack ?? 24,
|
|
160
|
+
maxRecords: maxRecords ?? 50,
|
|
161
|
+
});
|
|
162
|
+
const exceptionCount = result.logs.filter((log) => log.parsed?.hasException).length;
|
|
163
|
+
return {
|
|
164
|
+
structuredContent: result,
|
|
165
|
+
content: [
|
|
166
|
+
{
|
|
167
|
+
type: "text",
|
|
168
|
+
text: `Plugin trace logs:\n\n` +
|
|
169
|
+
`Total Logs: ${result.totalCount}\n` +
|
|
170
|
+
`Exceptions: ${exceptionCount}\n\n` +
|
|
171
|
+
`${JSON.stringify(result.logs, null, 2)}`,
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
console.error("Error getting plugin trace logs:", error);
|
|
178
|
+
return {
|
|
179
|
+
content: [
|
|
180
|
+
{
|
|
181
|
+
type: "text",
|
|
182
|
+
text: `Failed to get plugin trace logs: ${error.message}`,
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Register record tools with the MCP server.
|
|
4
|
+
*/
|
|
5
|
+
export function registerRecordTools(server, ctx) {
|
|
6
|
+
// Get Record
|
|
7
|
+
server.registerTool("get-record", {
|
|
8
|
+
title: "Get Record",
|
|
9
|
+
description: "Get a specific record by entity name (plural) and ID",
|
|
10
|
+
inputSchema: {
|
|
11
|
+
entityNamePlural: z.string().describe("The plural name of the entity (e.g., 'accounts', 'contacts')"),
|
|
12
|
+
recordId: z.string().describe("The GUID of the record"),
|
|
13
|
+
},
|
|
14
|
+
outputSchema: z.object({
|
|
15
|
+
entityNamePlural: z.string(),
|
|
16
|
+
recordId: z.string(),
|
|
17
|
+
record: z.any(),
|
|
18
|
+
}),
|
|
19
|
+
}, async ({ entityNamePlural, recordId }) => {
|
|
20
|
+
try {
|
|
21
|
+
const service = ctx.getRecordService();
|
|
22
|
+
const record = await service.getRecord(entityNamePlural, recordId);
|
|
23
|
+
return {
|
|
24
|
+
structuredContent: { entityNamePlural, recordId, record },
|
|
25
|
+
content: [
|
|
26
|
+
{
|
|
27
|
+
type: "text",
|
|
28
|
+
text: `Record from '${entityNamePlural}' with ID '${recordId}':\n\n${JSON.stringify(record, null, 2)}`,
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
console.error("Error getting record:", error);
|
|
35
|
+
return {
|
|
36
|
+
content: [
|
|
37
|
+
{
|
|
38
|
+
type: "text",
|
|
39
|
+
text: `Failed to get record: ${error.message}`,
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
// Query Records
|
|
46
|
+
server.registerTool("query-records", {
|
|
47
|
+
title: "Query Records",
|
|
48
|
+
description: "Query records using an OData filter expression",
|
|
49
|
+
inputSchema: {
|
|
50
|
+
entityNamePlural: z.string().describe("The plural name of the entity (e.g., 'accounts', 'contacts')"),
|
|
51
|
+
filter: z.string().describe("OData filter expression (e.g., \"name eq 'test'\" or \"createdon gt 2023-01-01\")"),
|
|
52
|
+
maxRecords: z.number().optional().describe("Maximum number of records to retrieve (default: 50)"),
|
|
53
|
+
},
|
|
54
|
+
outputSchema: z.object({
|
|
55
|
+
entityNamePlural: z.string(),
|
|
56
|
+
filter: z.string(),
|
|
57
|
+
count: z.number(),
|
|
58
|
+
records: z.any(),
|
|
59
|
+
}),
|
|
60
|
+
}, async ({ entityNamePlural, filter, maxRecords }) => {
|
|
61
|
+
try {
|
|
62
|
+
const service = ctx.getRecordService();
|
|
63
|
+
const records = await service.queryRecords(entityNamePlural, filter, maxRecords || 50);
|
|
64
|
+
const recordCount = records.value?.length || 0;
|
|
65
|
+
return {
|
|
66
|
+
structuredContent: { entityNamePlural, filter, count: recordCount, records },
|
|
67
|
+
content: [
|
|
68
|
+
{
|
|
69
|
+
type: "text",
|
|
70
|
+
text: `Retrieved ${recordCount} records from '${entityNamePlural}' with filter '${filter}':\n\n${JSON.stringify(records, null, 2)}`,
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
console.error("Error querying records:", error);
|
|
77
|
+
return {
|
|
78
|
+
content: [
|
|
79
|
+
{
|
|
80
|
+
type: "text",
|
|
81
|
+
text: `Failed to query records: ${error.message}`,
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
package/build/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "powerplatform-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "PowerPlatform Model Context Protocol server",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "tsc",
|
|
11
|
-
"prepublishOnly": "npm run build"
|
|
11
|
+
"prepublishOnly": "npm run build",
|
|
12
|
+
"start": "node build/index.js",
|
|
13
|
+
"dev": "npm run build && npm run start",
|
|
14
|
+
"inspector": "npm run build && dotenv -- npx @modelcontextprotocol/inspector node build/index.js"
|
|
12
15
|
},
|
|
13
16
|
"files": [
|
|
14
17
|
"build",
|
|
@@ -32,16 +35,18 @@
|
|
|
32
35
|
"url": "git+https://github.com/michsob/powerplatform-mcp.git"
|
|
33
36
|
},
|
|
34
37
|
"engines": {
|
|
35
|
-
"node": ">=
|
|
38
|
+
"node": ">=22.0.0 <25.0.0"
|
|
36
39
|
},
|
|
37
40
|
"dependencies": {
|
|
38
|
-
"@azure/msal-node": "^
|
|
39
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
40
|
-
"axios": "^1.
|
|
41
|
-
"zod": "^3.
|
|
41
|
+
"@azure/msal-node": "^5.0.2",
|
|
42
|
+
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
43
|
+
"axios": "^1.13.2",
|
|
44
|
+
"zod": "^4.3.5"
|
|
42
45
|
},
|
|
43
46
|
"devDependencies": {
|
|
44
47
|
"@types/node": "^22.13.10",
|
|
48
|
+
"dotenv": "^17.2.3",
|
|
49
|
+
"dotenv-cli": "^11.0.0",
|
|
45
50
|
"typescript": "^5.8.2"
|
|
46
51
|
}
|
|
47
52
|
}
|