powerplatform-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.
- package/LICENSE +21 -0
- package/README.md +52 -0
- package/build/PowerPlatformService.js +85 -0
- package/build/index.js +112 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Michal Sobieraj
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# PowerPlatform MCP Server
|
|
2
|
+
|
|
3
|
+
A Model Context Protocol (MCP) server for PowerPlatform/Dataverse entities. This tool provides access to entity metadata and attributes in the PowerPlatform environment.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
You can install and run this tool in two ways:
|
|
8
|
+
|
|
9
|
+
### Option 1: Install globally
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g powerplatform-mcp
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Then run it:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
powerplatform-mcp
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Option 2: Run directly with npx
|
|
22
|
+
|
|
23
|
+
Run without installing:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx powerplatform-mcp
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Configuration
|
|
30
|
+
|
|
31
|
+
Before running, set the following environment variables:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# PowerPlatform/Dataverse connection details
|
|
35
|
+
POWERPLATFORM_URL=https://yourenvironment.crm.dynamics.com
|
|
36
|
+
POWERPLATFORM_CLIENT_ID=your-azure-app-client-id
|
|
37
|
+
POWERPLATFORM_CLIENT_SECRET=your-azure-app-client-secret
|
|
38
|
+
POWERPLATFORM_TENANT_ID=your-azure-tenant-id
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
This is an MCP server designed to work with MCP-compatible clients like GitHub Copilot. Once running, it will expose tools for retrieving PowerPlatform entity metadata.
|
|
44
|
+
|
|
45
|
+
### Available Tools
|
|
46
|
+
|
|
47
|
+
- `get-entity-metadata`: Get metadata about a PowerPlatform entity
|
|
48
|
+
- `get-entity-attributes`: Get attributes/fields of a PowerPlatform entity
|
|
49
|
+
|
|
50
|
+
## License
|
|
51
|
+
|
|
52
|
+
MIT
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { ConfidentialClientApplication } from '@azure/msal-node';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
export class PowerPlatformService {
|
|
4
|
+
config;
|
|
5
|
+
msalClient;
|
|
6
|
+
accessToken = null;
|
|
7
|
+
tokenExpirationTime = 0;
|
|
8
|
+
constructor(config) {
|
|
9
|
+
this.config = config;
|
|
10
|
+
// Initialize MSAL client
|
|
11
|
+
this.msalClient = new ConfidentialClientApplication({
|
|
12
|
+
auth: {
|
|
13
|
+
clientId: this.config.clientId,
|
|
14
|
+
clientSecret: this.config.clientSecret,
|
|
15
|
+
authority: `https://login.microsoftonline.com/${this.config.tenantId}`,
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Get an access token for the PowerPlatform API
|
|
21
|
+
*/
|
|
22
|
+
async getAccessToken() {
|
|
23
|
+
const currentTime = Date.now();
|
|
24
|
+
// If we have a token that isn't expired, return it
|
|
25
|
+
if (this.accessToken && this.tokenExpirationTime > currentTime) {
|
|
26
|
+
return this.accessToken;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
// Get a new token
|
|
30
|
+
const result = await this.msalClient.acquireTokenByClientCredential({
|
|
31
|
+
scopes: [`${this.config.organizationUrl}/.default`],
|
|
32
|
+
});
|
|
33
|
+
if (!result || !result.accessToken) {
|
|
34
|
+
throw new Error('Failed to acquire access token');
|
|
35
|
+
}
|
|
36
|
+
this.accessToken = result.accessToken;
|
|
37
|
+
// Set expiration time (subtract 5 minutes to refresh early)
|
|
38
|
+
if (result.expiresOn) {
|
|
39
|
+
this.tokenExpirationTime = result.expiresOn.getTime() - (5 * 60 * 1000);
|
|
40
|
+
}
|
|
41
|
+
return this.accessToken;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
console.error('Error acquiring access token:', error);
|
|
45
|
+
throw new Error('Authentication failed');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Make an authenticated request to the PowerPlatform API
|
|
50
|
+
*/
|
|
51
|
+
async makeRequest(endpoint) {
|
|
52
|
+
try {
|
|
53
|
+
const token = await this.getAccessToken();
|
|
54
|
+
const response = await axios({
|
|
55
|
+
method: 'GET',
|
|
56
|
+
url: `${this.config.organizationUrl}/${endpoint}`,
|
|
57
|
+
headers: {
|
|
58
|
+
'Authorization': `Bearer ${token}`,
|
|
59
|
+
'Accept': 'application/json',
|
|
60
|
+
'OData-MaxVersion': '4.0',
|
|
61
|
+
'OData-Version': '4.0'
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
return response.data;
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
console.error('PowerPlatform API request failed:', error);
|
|
68
|
+
throw new Error(`PowerPlatform API request failed: ${error}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get metadata about an entity
|
|
73
|
+
* @param entityName The logical name of the entity
|
|
74
|
+
*/
|
|
75
|
+
async getEntityMetadata(entityName) {
|
|
76
|
+
return this.makeRequest(`api/data/v9.2/EntityDefinitions(LogicalName='${entityName}')`);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get metadata about entity attributes/fields
|
|
80
|
+
* @param entityName The logical name of the entity
|
|
81
|
+
*/
|
|
82
|
+
async getEntityAttributes(entityName) {
|
|
83
|
+
return this.makeRequest(`api/data/v9.2/EntityDefinitions(LogicalName='${entityName}')/Attributes`);
|
|
84
|
+
}
|
|
85
|
+
}
|
package/build/index.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { PowerPlatformService } from "./PowerPlatformService.js";
|
|
6
|
+
// Environment configuration
|
|
7
|
+
// These values can be set in environment variables or loaded from a configuration file
|
|
8
|
+
const POWERPLATFORM_CONFIG = {
|
|
9
|
+
organizationUrl: process.env.POWERPLATFORM_URL || "",
|
|
10
|
+
clientId: process.env.POWERPLATFORM_CLIENT_ID || "",
|
|
11
|
+
clientSecret: process.env.POWERPLATFORM_CLIENT_SECRET || "",
|
|
12
|
+
tenantId: process.env.POWERPLATFORM_TENANT_ID || "",
|
|
13
|
+
};
|
|
14
|
+
// Create server instance
|
|
15
|
+
const server = new McpServer({
|
|
16
|
+
name: "powerplatform-mcp",
|
|
17
|
+
version: "1.0.0",
|
|
18
|
+
});
|
|
19
|
+
let powerPlatformService = null;
|
|
20
|
+
// Function to initialize PowerPlatformService on demand
|
|
21
|
+
function getPowerPlatformService() {
|
|
22
|
+
if (!powerPlatformService) {
|
|
23
|
+
// Check if configuration is complete
|
|
24
|
+
const missingConfig = [];
|
|
25
|
+
if (!POWERPLATFORM_CONFIG.organizationUrl)
|
|
26
|
+
missingConfig.push("organizationUrl");
|
|
27
|
+
if (!POWERPLATFORM_CONFIG.clientId)
|
|
28
|
+
missingConfig.push("clientId");
|
|
29
|
+
if (!POWERPLATFORM_CONFIG.clientSecret)
|
|
30
|
+
missingConfig.push("clientSecret");
|
|
31
|
+
if (!POWERPLATFORM_CONFIG.tenantId)
|
|
32
|
+
missingConfig.push("tenantId");
|
|
33
|
+
if (missingConfig.length > 0) {
|
|
34
|
+
throw new Error(`Missing PowerPlatform configuration: ${missingConfig.join(", ")}. Set these in environment variables.`);
|
|
35
|
+
}
|
|
36
|
+
// Initialize service
|
|
37
|
+
powerPlatformService = new PowerPlatformService(POWERPLATFORM_CONFIG);
|
|
38
|
+
console.error("PowerPlatform service initialized");
|
|
39
|
+
}
|
|
40
|
+
return powerPlatformService;
|
|
41
|
+
}
|
|
42
|
+
// PowerPlatform entity metadata
|
|
43
|
+
server.tool("get-entity-metadata", "Get metadata about a PowerPlatform entity", {
|
|
44
|
+
entityName: z.string().describe("The logical name of the entity"),
|
|
45
|
+
}, async ({ entityName }) => {
|
|
46
|
+
try {
|
|
47
|
+
// Get or initialize PowerPlatformService
|
|
48
|
+
const service = getPowerPlatformService();
|
|
49
|
+
const metadata = await service.getEntityMetadata(entityName);
|
|
50
|
+
// Format the metadata as a string for text display
|
|
51
|
+
const metadataStr = JSON.stringify(metadata, null, 2);
|
|
52
|
+
return {
|
|
53
|
+
content: [
|
|
54
|
+
{
|
|
55
|
+
type: "text",
|
|
56
|
+
text: `Entity metadata for '${entityName}':\n\n${metadataStr}`,
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.error("Error getting entity metadata:", error);
|
|
63
|
+
return {
|
|
64
|
+
content: [
|
|
65
|
+
{
|
|
66
|
+
type: "text",
|
|
67
|
+
text: `Failed to get entity metadata: ${error}`,
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
// PowerPlatform entity attributes
|
|
74
|
+
server.tool("get-entity-attributes", "Get attributes/fields of a PowerPlatform entity", {
|
|
75
|
+
entityName: z.string().describe("The logical name of the entity"),
|
|
76
|
+
}, async ({ entityName }) => {
|
|
77
|
+
try {
|
|
78
|
+
// Get or initialize PowerPlatformService
|
|
79
|
+
const service = getPowerPlatformService();
|
|
80
|
+
const attributes = await service.getEntityAttributes(entityName);
|
|
81
|
+
// Format the attributes as a string for text display
|
|
82
|
+
const attributesStr = JSON.stringify(attributes, null, 2);
|
|
83
|
+
return {
|
|
84
|
+
content: [
|
|
85
|
+
{
|
|
86
|
+
type: "text",
|
|
87
|
+
text: `Attributes for entity '${entityName}':\n\n${attributesStr}`,
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
console.error("Error getting entity attributes:", error);
|
|
94
|
+
return {
|
|
95
|
+
content: [
|
|
96
|
+
{
|
|
97
|
+
type: "text",
|
|
98
|
+
text: `Failed to get entity attributes: ${error}`,
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
async function main() {
|
|
105
|
+
const transport = new StdioServerTransport();
|
|
106
|
+
await server.connect(transport);
|
|
107
|
+
console.error("Initializing PowerPlatform MCP Server...");
|
|
108
|
+
}
|
|
109
|
+
main().catch((error) => {
|
|
110
|
+
console.error("Fatal error in main():", error);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "powerplatform-mcp",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "PowerPlatform Model Context Protocol server",
|
|
5
|
+
"main": "build/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"powerplatform-mcp": "./build/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"prepublishOnly": "npm run build"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"build",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"keywords": [
|
|
18
|
+
"powerplatform",
|
|
19
|
+
"mcp",
|
|
20
|
+
"model-context-protocol",
|
|
21
|
+
"dynamics",
|
|
22
|
+
"dataverse"
|
|
23
|
+
],
|
|
24
|
+
"author": "Michal Sobieraj",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/michsob/powerplatform-mcp.git"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=16.0.0"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@azure/msal-node": "^3.3.0",
|
|
39
|
+
"@modelcontextprotocol/sdk": "^1.7.0",
|
|
40
|
+
"axios": "^1.8.3",
|
|
41
|
+
"zod": "^3.24.2"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^22.13.10",
|
|
45
|
+
"typescript": "^5.8.2"
|
|
46
|
+
}
|
|
47
|
+
}
|