Dynamics 365 MCP Server

/** * Functions for Dynamics 365 authentication and user identity */ // Import required types import { ConfidentialClientApplication, } from "@azure/msal-node"; import dotenv from "dotenv"; // Load environment variables from .env file dotenv.config(); export class Dynamics365 { clientId; clientSecret; tenantId; d365Url; msalInstance; /** * @param clientId - Azure AD application client ID * @param clientSecret - Azure AD application client secret * @param tenantId - Azure AD tenant ID * @param d365Url - Dynamics 365 instance URL (e.g., https://your-org.crm.dynamics.com) */ constructor(clientId, clientSecret, tenantId, d365Url) { this.clientId = clientId; this.clientSecret = clientSecret; this.tenantId = tenantId; this.d365Url = d365Url; // Configure MSAL const msalConfig = { auth: { clientId: this.clientId, authority: `https://login.microsoftonline.com/${this.tenantId}`, clientSecret: this.clientSecret, }, }; // Initialize MSAL client this.msalInstance = new ConfidentialClientApplication(msalConfig); } /** * @returns Promise resolving to authentication result with token */ async authenticate() { const tokenRequest = { scopes: [`${new URL(this.d365Url).origin}/.default`], }; try { const response = await this.msalInstance.acquireTokenByClientCredential(tokenRequest); if (response && response.accessToken) { return response.accessToken; } else { throw new Error("Token acquisition failed: response is null or invalid."); } } catch (error) { console.error("Token acquisition failed:", error); throw new Error(`Failed to authenticate with Dynamics 365: ${error instanceof Error ? error.message : String(error)}`); } } /** * Makes an API request to Dynamics 365 * @param endpoint - The API endpoint (relative to the base URL) * @param method - The HTTP method (e.g., "GET", "POST") * @param body - The request body (optional, for POST/PUT requests) * @param additionalHeaders - Additional headers to include in the request * @returns Promise resolving to the API response */ async makeApiRequest(endpoint, method, body, additionalHeaders) { const token = await this.authenticate(); const baseUrl = this.d365Url.endsWith("/") ? this.d365Url : `${this.d365Url}/`; const url = `${baseUrl}${endpoint}`; const headers = { Authorization: `Bearer ${token}`, Accept: "application/json", "Content-Type": "application/json", "OData-MaxVersion": "4.0", "OData-Version": "4.0", ...additionalHeaders, }; try { const response = await fetch(url, { method, headers, body: body ? JSON.stringify(body) : undefined, }); if (!response.ok) { throw new Error(`API request failed with status: ${response.status}, message: ${await response.text()}`); } return await response.json(); } catch (error) { console.error(`API request to ${url} failed:`, error); throw new Error(`Failed to make API request: ${error instanceof Error ? error.message : String(error)}`); } } /** * Makes a WhoAmI request to Dynamics 365 to get information about the currently logged-in user * @returns Promise resolving to the user's information */ async makeWhoAmIRequest() { const data = await this.makeApiRequest("api/data/v9.2/WhoAmI", "GET"); // If we want to get more details about the user, we can make an additional request if (data && data.UserId) { const userDetails = await this.makeApiRequest(`api/data/v9.2/systemusers(${data.UserId})`, "GET", undefined, { Prefer: 'odata.include-annotations="*"' }); data.UserName = userDetails.domainname; data.FullName = userDetails.fullname; } return data; } /** * Fetches accounts from Dynamics 365 * @returns Promise resolving to the list of accounts */ async getAccounts() { return this.makeApiRequest("api/data/v9.2/accounts", "GET"); } /** * Fetches contacts for a given account from Dynamics 365 * @param accountId - The ID of the account for which to retrieve contacts * @returns Promise resolving to the list of contacts */ async getAssociatedContacts(accountId) { if (!accountId) { throw new Error("Account ID is required to fetch contacts."); } const endpoint = `api/data/v9.2/contacts?$filter=_parentcustomerid_value eq ${accountId}`; return this.makeApiRequest(endpoint, "GET"); } /** * Fetches opportunities for a given account from Dynamics 365 * @param accountId - The ID of the account for which to retrieve opportunities * @returns Promise resolving to the list of opportunities */ async getAssociatedOpportunities(accountId) { if (!accountId) { throw new Error("Account ID is required to fetch opportunities."); } const endpoint = `api/data/v9.2/opportunities?$filter=_customerid_value eq ${accountId}`; return this.makeApiRequest(endpoint, "GET"); } /* create a new account in Dynamics 365 * @param accountData - The data for the new account * @returns Promise resolving to the created account */ async createAccount(accountData) { if (!accountData) { throw new Error("Account data is required to create an account."); } const endpoint = "api/data/v9.2/accounts"; return this.makeApiRequest(endpoint, "POST", accountData); } /** * Updates an existing account in Dynamics 365 * @param accountId - The ID of the account to update * @param accountData - The updated data for the account * @returns Promise resolving to the updated account */ async updateAccount(accountId, accountData) { if (!accountId) { throw new Error("Account ID is required to update an account."); } if (!accountData) { throw new Error("Account data is required to update an account."); } const endpoint = `api/data/v9.2/accounts(${accountId})`; return this.makeApiRequest(endpoint, "PATCH", accountData); } }
ID: duod0pskh9