@mcp-consultant-tools/powerplatform-data
Version:
MCP server for PowerPlatform data CRUD operations (operational use)
208 lines • 8.96 kB
JavaScript
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
import { pathToFileURL } from 'node:url';
import { realpathSync } from 'node:fs';
import { createMcpServer, createEnvLoader } from '@mcp-consultant-tools/core';
import { PowerPlatformService } from './PowerPlatformService.js';
/**
* Register powerplatform-data tools with an MCP server
* @param server - MCP server instance
* @param service - Optional pre-initialized PowerPlatformService (for testing)
*/
export function registerPowerplatformDataTools(server, service) {
let ppService = service || null;
function getPowerPlatformService() {
if (!ppService) {
const requiredVars = [
'POWERPLATFORM_URL',
'POWERPLATFORM_CLIENT_ID',
'POWERPLATFORM_CLIENT_SECRET',
'POWERPLATFORM_TENANT_ID'
];
const missing = requiredVars.filter(v => !process.env[v]);
if (missing.length > 0) {
throw new Error(`Missing required PowerPlatform configuration: ${missing.join(', ')}`);
}
const config = {
organizationUrl: process.env.POWERPLATFORM_URL,
clientId: process.env.POWERPLATFORM_CLIENT_ID,
clientSecret: process.env.POWERPLATFORM_CLIENT_SECRET,
tenantId: process.env.POWERPLATFORM_TENANT_ID,
};
ppService = new PowerPlatformService(config);
}
return ppService;
}
// Permission check functions
function checkCreateEnabled() {
if (process.env.POWERPLATFORM_ENABLE_CREATE !== 'true') {
throw new Error('Create operations are disabled. Set POWERPLATFORM_ENABLE_CREATE=true to enable.');
}
}
function checkUpdateEnabled() {
if (process.env.POWERPLATFORM_ENABLE_UPDATE !== 'true') {
throw new Error('Update operations are disabled. Set POWERPLATFORM_ENABLE_UPDATE=true to enable.');
}
}
function checkDeleteEnabled() {
if (process.env.POWERPLATFORM_ENABLE_DELETE !== 'true') {
throw new Error('Delete operations are disabled. Set POWERPLATFORM_ENABLE_DELETE=true to enable.');
}
}
// Tool registrations
server.tool("create-record", "Create a new record in Dataverse. Requires POWERPLATFORM_ENABLE_CREATE=true.", {
entityNamePlural: z
.string()
.describe("The plural name of the entity (e.g., 'accounts', 'contacts', 'sic_applications')"),
data: z
.record(z.any())
.describe("Record data as JSON object. Field names must match logical names (e.g., {'name': 'Acme Corp', 'telephone1': '555-1234'}). " +
"For lookup fields, use '@odata.bind' syntax: {'parentaccountid@odata.bind': '/accounts(guid)'}. " +
"For option sets, use integer values."),
}, async ({ entityNamePlural, data }) => {
try {
checkCreateEnabled();
const service = getPowerPlatformService();
const result = await service.createRecord(entityNamePlural, data);
return {
content: [
{
type: "text",
text: `✅ Record created successfully in ${entityNamePlural}\n\n` +
`**Record ID:** ${result.id || result[Object.keys(result).find(k => k.endsWith('id')) || ''] || 'N/A'}\n\n` +
`**Created Record:**\n\`\`\`json\n${JSON.stringify(result, null, 2)}\n\`\`\``,
},
],
};
}
catch (error) {
console.error("Error creating record:", error);
return {
content: [
{
type: "text",
text: `❌ Failed to create record: ${error.message}`,
},
],
isError: true,
};
}
});
server.tool("update-record", "Update an existing record in Dataverse. Requires POWERPLATFORM_ENABLE_UPDATE=true.", {
entityNamePlural: z
.string()
.describe("The plural name of the entity (e.g., 'accounts', 'contacts', 'sic_applications')"),
recordId: z
.string()
.describe("The GUID of the record to update"),
data: z
.record(z.any())
.describe("Partial record data to update (only fields being changed). " +
"Field names must match logical names. " +
"Use '@odata.bind' syntax for lookups, integer values for option sets."),
}, async ({ entityNamePlural, recordId, data }) => {
try {
checkUpdateEnabled();
const service = getPowerPlatformService();
const result = await service.updateRecord(entityNamePlural, recordId, data);
return {
content: [
{
type: "text",
text: `✅ Record updated successfully in ${entityNamePlural}\n\n` +
`**Record ID:** ${recordId}\n\n` +
`**Updated Record:**\n\`\`\`json\n${JSON.stringify(result, null, 2)}\n\`\`\``,
},
],
};
}
catch (error) {
console.error("Error updating record:", error);
return {
content: [
{
type: "text",
text: `❌ Failed to update record: ${error.message}`,
},
],
isError: true,
};
}
});
server.tool("delete-record", "Delete a record from Dataverse. Requires POWERPLATFORM_ENABLE_DELETE=true. WARNING: This operation is permanent and cannot be undone.", {
entityNamePlural: z
.string()
.describe("The plural name of the entity (e.g., 'accounts', 'contacts', 'sic_applications')"),
recordId: z
.string()
.describe("The GUID of the record to delete"),
confirm: z
.boolean()
.optional()
.describe("Confirmation flag - must be true to proceed with deletion (safety check)"),
}, async ({ entityNamePlural, recordId, confirm }) => {
try {
checkDeleteEnabled();
// Require explicit confirmation for deletion
if (confirm !== true) {
return {
content: [
{
type: "text",
text: `⚠️ Delete operation requires explicit confirmation.\n\n` +
`You are about to delete record **${recordId}** from **${entityNamePlural}**.\n\n` +
`This operation is **permanent** and **cannot be undone**.\n\n` +
`To proceed, call this tool again with \`confirm: true\`.`,
},
],
};
}
const service = getPowerPlatformService();
await service.deleteRecord(entityNamePlural, recordId);
return {
content: [
{
type: "text",
text: `✅ Record deleted successfully\n\n` +
`**Entity:** ${entityNamePlural}\n` +
`**Record ID:** ${recordId}\n\n` +
`⚠️ This operation is permanent.`,
},
],
};
}
catch (error) {
console.error("Error deleting record:", error);
return {
content: [
{
type: "text",
text: `❌ Failed to delete record: ${error.message}`,
},
],
isError: true,
};
}
});
console.error(`✅ powerplatform-data tools registered (${3} tools)`);
}
// CLI entry point (standalone execution)
// Uses realpathSync to resolve symlinks created by npx
if (import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) {
const loadEnv = createEnvLoader();
loadEnv();
const server = createMcpServer({
name: '@mcp-consultant-tools/powerplatform-data',
version: '1.0.0',
capabilities: { tools: {}, prompts: {} }
});
registerPowerplatformDataTools(server);
const transport = new StdioServerTransport();
server.connect(transport).catch((error) => {
console.error('Failed to start powerplatform-data MCP server:', error);
process.exit(1);
});
console.error('powerplatform-data MCP server running');
}
//# sourceMappingURL=index.js.map