Skip to main content
Glama
mwhesse

Dataverse MCP Server

by mwhesse

generate_powerpages_webapi_call

Create PowerPages API calls and JavaScript examples for Dataverse operations including retrieve, create, update, and delete with authentication context and portal-specific patterns.

Instructions

Generate PowerPages-specific API calls, JavaScript examples, and React components for Dataverse operations through PowerPages portals. Includes authentication context and portal-specific patterns.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
baseUrlNoPowerPages site base URL (e.g., 'https://yoursite.powerappsportals.com')
countNoInclude count of records
customHeadersNoCustom headers to include in the request
dataNoData to send in request body for create/update operations
entityIdNoEntity ID for single record operations (GUID)
expandNoRelated entities to expand
filterNoOData filter expression
includeAuthContextNoInclude authentication context information
logicalEntityNameYesLogical entity name (e.g., 'cr7ae_creditcardse', 'contact') - will be automatically suffixed with 's' for PowerPages API URLs
operationYesType of operation to perform
orderbyNoOData orderby expression
requestVerificationTokenNoInclude __RequestVerificationToken placeholder for POST operations
selectNoFields to select (e.g., ['cr7ae_name', 'cr7ae_type'])
skipNoNumber of records to skip
topNoNumber of records to return

Implementation Reference

  • The main execution handler for the tool. Handles all operations (retrieve, create, etc.), resolves entity schema, builds OData queries and bodies with @odata.bind processing, generates HTTP/cURL/JS/React code examples, and additional context like auth and schema.
    async (params: any) => { try { const baseUrl = params.baseUrl || 'https://yoursite.powerappsportals.com'; // Resolve entity metadata for schema-aware capabilities let entityInfo: any = null; let entitySetName = ''; let targetSetCache = new Map<string, string>(); try { entityInfo = await resolvePowerPagesEntityInfo(client, params.logicalEntityName); entitySetName = entityInfo.entitySetName; } catch (error) { // Fallback to naive pluralization if metadata fails entitySetName = params.logicalEntityName ? (params.logicalEntityName.endsWith('s') ? params.logicalEntityName : `${params.logicalEntityName}s`) : ''; } let url = `${baseUrl}/_api/${entitySetName}`; let method = 'GET'; let body: any = null; let headers: Record<string, string> = { 'Accept': 'application/json', 'Content-Type': 'application/json', ...params.customHeaders }; // Add request verification token for POST operations if requested if (params.requestVerificationToken && ['create', 'update', 'delete'].includes(params.operation)) { headers['__RequestVerificationToken'] = '{{REQUEST_VERIFICATION_TOKEN}}'; } // Helper to resolve target entity set names const resolveTargetSet = async (targetLogicalName: string): Promise<string> => { return await getPowerPagesTargetEntitySetName(client, targetSetCache, targetLogicalName); }; switch (params.operation) { case 'retrieve': if (!params.entityId) { throw new Error("entityId is required for retrieve operation"); } url += `(${params.entityId})`; // Auto-select primary fields if no select specified and we have schema let finalSelect = params.select; if (!params.select && entityInfo && entityInfo.primaryIdAttribute) { const autoFields = [entityInfo.primaryIdAttribute]; if (entityInfo.primaryNameAttribute) { autoFields.push(entityInfo.primaryNameAttribute); } finalSelect = autoFields; } const queryParams = buildPowerPagesODataQuery({ select: finalSelect, expand: params.expand }); if (queryParams) { url += queryParams; } break; case 'retrieveMultiple': // Auto-select primary fields if no select specified and we have schema let finalListSelect = params.select; if (!params.select && entityInfo && entityInfo.primaryIdAttribute) { const autoFields = [entityInfo.primaryIdAttribute]; if (entityInfo.primaryNameAttribute) { autoFields.push(entityInfo.primaryNameAttribute); } finalListSelect = autoFields; } const listQueryParams = buildPowerPagesODataQuery({ select: finalListSelect, filter: params.filter, orderby: params.orderby, top: params.top, skip: params.skip, expand: params.expand, count: params.count }); if (listQueryParams) { url += listQueryParams; } break; case 'create': method = 'POST'; // Process @odata.bind properties and generate sample if no data provided if (params.data) { body = processPowerPagesODataBindProperties(params.data, baseUrl, entityInfo); } else if (entityInfo) { // Generate schema-aware sample body body = await generatePowerPagesSampleBodyFromSchema(entityInfo, baseUrl, resolveTargetSet, 'create'); } else { body = {}; } break; case 'update': if (!params.entityId) { throw new Error("entityId is required for update operation"); } method = 'PATCH'; url += `(${params.entityId})`; // Process @odata.bind properties and generate sample if no data provided if (params.data) { body = processPowerPagesODataBindProperties(params.data, baseUrl, entityInfo); } else if (entityInfo) { // Generate schema-aware sample body body = await generatePowerPagesSampleBodyFromSchema(entityInfo, baseUrl, resolveTargetSet, 'update'); } else { body = {}; } break; case 'delete': if (!params.entityId) { throw new Error("entityId is required for delete operation"); } method = 'DELETE'; url += `(${params.entityId})`; break; default: throw new Error(`Unsupported operation: ${params.operation}`); } // Generate examples const examples = []; // HTTP Request const httpRequest = [ `${method} ${url} HTTP/1.1`, `Host: ${new URL(baseUrl).host}`, ...Object.entries(headers).map(([key, value]) => `${key}: ${value}`) ]; if (body) { httpRequest.push(''); httpRequest.push(JSON.stringify(body, null, 2)); } examples.push({ title: "HTTP Request", content: httpRequest.join('\n') }); // cURL Command const curlParts = [`curl -X ${method}`]; Object.entries(headers).forEach(([key, value]) => { curlParts.push(`-H "${key}: ${value}"`); }); if (body) { curlParts.push(`-d '${JSON.stringify(body)}'`); } curlParts.push(`"${url}"`); examples.push({ title: "cURL Command", content: curlParts.join(' \\\n ') }); // JavaScript Fetch const fetchOptions: any = { method, headers }; if (body) { fetchOptions.body = JSON.stringify(body); } const jsCode = ` // PowerPages WebAPI ${params.operation} operation fetch('${url}', ${JSON.stringify(fetchOptions, null, 2)}) .then(response => { if (!response.ok) { throw new Error(\`HTTP error! status: \${response.status}\`); } return response.json(); }) .then(data => { console.log('Success:', data); }) .catch(error => { console.error('Error:', error); });`; examples.push({ title: "JavaScript (Fetch API)", content: jsCode.trim() }); // React Component Example const reactCode = ` import React, { useState, useEffect } from 'react'; const ${params.operation.charAt(0).toUpperCase() + params.operation.slice(1)}Component = () => { const [data, setData] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const perform${params.operation.charAt(0).toUpperCase() + params.operation.slice(1)} = async () => { setLoading(true); setError(null); try { const response = await fetch('${url}', ${JSON.stringify(fetchOptions, null, 6)}); if (!response.ok) { throw new Error(\`HTTP error! status: \${response.status}\`); } const result = await response.json(); setData(result); } catch (err) { setError(err.message); } finally { setLoading(false); } }; ${params.operation === 'retrieveMultiple' || params.operation === 'retrieve' ? ` useEffect(() => { perform${params.operation.charAt(0).toUpperCase() + params.operation.slice(1)}(); }, []);` : ''} return ( <div> <h3>${params.operation.charAt(0).toUpperCase() + params.operation.slice(1)} ${params.logicalEntityName || 'Entity'}</h3> ${params.operation !== 'retrieveMultiple' && params.operation !== 'retrieve' ? ` <button onClick={perform${params.operation.charAt(0).toUpperCase() + params.operation.slice(1)}} disabled={loading}> {loading ? 'Processing...' : '${params.operation.charAt(0).toUpperCase() + params.operation.slice(1)}'} </button>` : ''} {loading && <p>Loading...</p>} {error && <p style={{color: 'red'}}>Error: {error}</p>} {data && ( <div> <h4>Result:</h4> <pre>{JSON.stringify(data, null, 2)}</pre> </div> )} </div> ); }; export default ${params.operation.charAt(0).toUpperCase() + params.operation.slice(1)}Component;`; examples.push({ title: "React Component", content: reactCode.trim() }); // Add @odata.bind examples if present in the body if (body && hasODataBindProperties(body)) { const bindExamples = extractNavigationPropertyExamples(body); if (bindExamples.length > 0) { const odataBindInfo = ` ## @odata.bind Relationship Examples The request body includes relationship associations using @odata.bind: \`\`\`javascript ${bindExamples.join('\n')} \`\`\` ### @odata.bind Usage Patterns: 1. **Associate with existing record:** \`"navigationProperty@odata.bind": "/_api/entityset(guid)"\` 2. **Disassociate relationship:** \`"navigationProperty@odata.bind": null\` 3. **PowerPages URL Format:** - Use relative paths: \`/_api/contacts(guid)\` - Entity set names are typically plural: \`contacts\`, \`accounts\`, etc. ### Navigation Property Names: ${entityInfo && entityInfo.lookupNavMap.size > 0 ? Array.from(entityInfo.lookupNavMap.entries()) .map((entry: any) => { const [attr, nav] = entry as [string, string]; return `- Lookup attribute \`${attr}\` → Navigation property \`${nav}\``; }) .join('\n') : '- Navigation properties are automatically resolved from table schema'}`; examples.push({ title: "@odata.bind Relationships", content: odataBindInfo.trim() }); } } // Authentication context if requested if (params.includeAuthContext) { const authInfo = ` ## Authentication Context for PowerPages PowerPages uses different authentication mechanisms: 1. **Anonymous Access**: No authentication required for public data 2. **Authenticated Users**: Session-based authentication via portal login 3. **Request Verification Token**: Anti-CSRF protection for state-changing operations ### Getting Request Verification Token (JavaScript): \`\`\`javascript // Get the token from the page (usually in a hidden input or meta tag) const token = document.querySelector('input[name="__RequestVerificationToken"]')?.value || document.querySelector('meta[name="__RequestVerificationToken"]')?.content; // Include in headers for POST/PATCH/DELETE operations headers['__RequestVerificationToken'] = token; \`\`\` ### User Context: \`\`\`javascript // Access current user information (if available) const userContext = { isAuthenticated: window.Shell?.user?.isAuthenticated || false, userId: window.Shell?.user?.id, userName: window.Shell?.user?.displayName }; \`\`\``; examples.push({ title: "Authentication Information", content: authInfo.trim() }); } // Add schema information if available if (entityInfo && entityInfo.logicalName) { const schemaInfo = ` ## Schema Information **Entity:** ${entityInfo.logicalName} (${entityInfo.entitySetName}) **Primary ID:** ${entityInfo.primaryIdAttribute || 'Not available'} **Primary Name:** ${entityInfo.primaryNameAttribute || 'Not available'} ### Available Fields: ${entityInfo.attributes && entityInfo.attributes.length > 0 ? entityInfo.attributes .filter((attr: any) => attr?.LogicalName) .slice(0, 10) // Show first 10 fields .map((attr: any) => `- \`${attr.LogicalName}\` (${attr.AttributeType})`) .join('\n') + (entityInfo.attributes.length > 10 ? `\n- ... and ${entityInfo.attributes.length - 10} more fields` : '') : 'Schema information not available'} ### Lookup Navigation Properties: ${entityInfo.lookupNavMap && entityInfo.lookupNavMap.size > 0 ? Array.from(entityInfo.lookupNavMap.entries()) .map((entry: any) => { const [attr, nav] = entry as [string, string]; return `- \`${attr}\` → \`${nav}\``; }) .join('\n') : 'No lookup relationships found'}`; examples.push({ title: "Entity Schema", content: schemaInfo.trim() }); } const result = examples.map(example => `## ${example.title}\n\n\`\`\`${example.title.includes('React') ? 'jsx' : example.title.includes('JavaScript') ? 'javascript' : example.title.includes('cURL') ? 'bash' : example.title.includes('@odata.bind') || example.title.includes('Schema') || example.title.includes('Authentication') ? 'markdown' : 'http'}\n${example.content}\n\`\`\`` ).join('\n\n'); return { content: [ { type: "text", text: result } ] }; } catch (error) { return { content: [ { type: "text", text: `Error generating PowerPages WebAPI call: ${error instanceof Error ? error.message : 'Unknown error'}` } ], isError: true }; } } );
  • Zod input schema defining parameters for the tool: operation type, entity name/ID, OData query options, request data, PowerPages-specific options like baseUrl and requestVerificationToken.
    inputSchema: { operation: z.enum([ "retrieve", "retrieveMultiple", "create", "update", "delete" ]).describe("Type of operation to perform"), logicalEntityName: z.string().describe("Logical entity name (e.g., 'cr7ae_creditcardse', 'contact') - will be automatically suffixed with 's' for PowerPages API URLs"), entityId: z.string().optional().describe("Entity ID for single record operations (GUID)"), // OData query options select: z.array(z.string()).optional().describe("Fields to select (e.g., ['cr7ae_name', 'cr7ae_type'])"), filter: z.string().optional().describe("OData filter expression"), orderby: z.string().optional().describe("OData orderby expression"), top: z.number().optional().describe("Number of records to return"), skip: z.number().optional().describe("Number of records to skip"), expand: z.string().optional().describe("Related entities to expand"), count: z.boolean().optional().describe("Include count of records"), // Request body for create/update operations data: z.record(z.any()).optional().describe("Data to send in request body for create/update operations"), // PowerPages specific options baseUrl: z.string().optional().describe("PowerPages site base URL (e.g., 'https://yoursite.powerappsportals.com')"), requestVerificationToken: z.boolean().default(false).describe("Include __RequestVerificationToken placeholder for POST operations"), includeAuthContext: z.boolean().default(false).describe("Include authentication context information"), // Additional headers customHeaders: z.record(z.string()).optional().describe("Custom headers to include in the request") }
  • Factory function that registers the MCP tool with name 'generate_powerpages_webapi_call', title, description, input schema, and handler callback.
    export function generatePowerPagesWebAPICallTool(server: McpServer, client: DataverseClient) { server.registerTool( "generate_powerpages_webapi_call", { title: "Generate PowerPages WebAPI Call", description: "Generate PowerPages-specific API calls, JavaScript examples, and React components for Dataverse operations through PowerPages portals. Includes authentication context and portal-specific patterns.", inputSchema: { operation: z.enum([ "retrieve", "retrieveMultiple", "create", "update", "delete" ]).describe("Type of operation to perform"), logicalEntityName: z.string().describe("Logical entity name (e.g., 'cr7ae_creditcardse', 'contact') - will be automatically suffixed with 's' for PowerPages API URLs"), entityId: z.string().optional().describe("Entity ID for single record operations (GUID)"), // OData query options select: z.array(z.string()).optional().describe("Fields to select (e.g., ['cr7ae_name', 'cr7ae_type'])"), filter: z.string().optional().describe("OData filter expression"), orderby: z.string().optional().describe("OData orderby expression"), top: z.number().optional().describe("Number of records to return"), skip: z.number().optional().describe("Number of records to skip"), expand: z.string().optional().describe("Related entities to expand"), count: z.boolean().optional().describe("Include count of records"), // Request body for create/update operations data: z.record(z.any()).optional().describe("Data to send in request body for create/update operations"), // PowerPages specific options baseUrl: z.string().optional().describe("PowerPages site base URL (e.g., 'https://yoursite.powerappsportals.com')"), requestVerificationToken: z.boolean().default(false).describe("Include __RequestVerificationToken placeholder for POST operations"), includeAuthContext: z.boolean().default(false).describe("Include authentication context information"), // Additional headers customHeaders: z.record(z.string()).optional().describe("Custom headers to include in the request") } }, async (params: any) => { try { const baseUrl = params.baseUrl || 'https://yoursite.powerappsportals.com'; // Resolve entity metadata for schema-aware capabilities let entityInfo: any = null; let entitySetName = ''; let targetSetCache = new Map<string, string>(); try { entityInfo = await resolvePowerPagesEntityInfo(client, params.logicalEntityName); entitySetName = entityInfo.entitySetName; } catch (error) { // Fallback to naive pluralization if metadata fails entitySetName = params.logicalEntityName ? (params.logicalEntityName.endsWith('s') ? params.logicalEntityName : `${params.logicalEntityName}s`) : ''; } let url = `${baseUrl}/_api/${entitySetName}`; let method = 'GET'; let body: any = null; let headers: Record<string, string> = { 'Accept': 'application/json', 'Content-Type': 'application/json', ...params.customHeaders }; // Add request verification token for POST operations if requested if (params.requestVerificationToken && ['create', 'update', 'delete'].includes(params.operation)) { headers['__RequestVerificationToken'] = '{{REQUEST_VERIFICATION_TOKEN}}'; } // Helper to resolve target entity set names const resolveTargetSet = async (targetLogicalName: string): Promise<string> => { return await getPowerPagesTargetEntitySetName(client, targetSetCache, targetLogicalName); }; switch (params.operation) { case 'retrieve': if (!params.entityId) { throw new Error("entityId is required for retrieve operation"); } url += `(${params.entityId})`; // Auto-select primary fields if no select specified and we have schema let finalSelect = params.select; if (!params.select && entityInfo && entityInfo.primaryIdAttribute) { const autoFields = [entityInfo.primaryIdAttribute]; if (entityInfo.primaryNameAttribute) { autoFields.push(entityInfo.primaryNameAttribute); } finalSelect = autoFields; } const queryParams = buildPowerPagesODataQuery({ select: finalSelect, expand: params.expand }); if (queryParams) { url += queryParams; } break; case 'retrieveMultiple': // Auto-select primary fields if no select specified and we have schema let finalListSelect = params.select; if (!params.select && entityInfo && entityInfo.primaryIdAttribute) { const autoFields = [entityInfo.primaryIdAttribute]; if (entityInfo.primaryNameAttribute) { autoFields.push(entityInfo.primaryNameAttribute); } finalListSelect = autoFields; } const listQueryParams = buildPowerPagesODataQuery({ select: finalListSelect, filter: params.filter, orderby: params.orderby, top: params.top, skip: params.skip, expand: params.expand, count: params.count }); if (listQueryParams) { url += listQueryParams; } break; case 'create': method = 'POST'; // Process @odata.bind properties and generate sample if no data provided if (params.data) { body = processPowerPagesODataBindProperties(params.data, baseUrl, entityInfo); } else if (entityInfo) { // Generate schema-aware sample body body = await generatePowerPagesSampleBodyFromSchema(entityInfo, baseUrl, resolveTargetSet, 'create'); } else { body = {}; } break; case 'update': if (!params.entityId) { throw new Error("entityId is required for update operation"); } method = 'PATCH'; url += `(${params.entityId})`; // Process @odata.bind properties and generate sample if no data provided if (params.data) { body = processPowerPagesODataBindProperties(params.data, baseUrl, entityInfo); } else if (entityInfo) { // Generate schema-aware sample body body = await generatePowerPagesSampleBodyFromSchema(entityInfo, baseUrl, resolveTargetSet, 'update'); } else { body = {}; } break; case 'delete': if (!params.entityId) { throw new Error("entityId is required for delete operation"); } method = 'DELETE'; url += `(${params.entityId})`; break; default: throw new Error(`Unsupported operation: ${params.operation}`); } // Generate examples const examples = []; // HTTP Request const httpRequest = [ `${method} ${url} HTTP/1.1`, `Host: ${new URL(baseUrl).host}`, ...Object.entries(headers).map(([key, value]) => `${key}: ${value}`) ]; if (body) { httpRequest.push(''); httpRequest.push(JSON.stringify(body, null, 2)); } examples.push({ title: "HTTP Request", content: httpRequest.join('\n') }); // cURL Command const curlParts = [`curl -X ${method}`]; Object.entries(headers).forEach(([key, value]) => { curlParts.push(`-H "${key}: ${value}"`); }); if (body) { curlParts.push(`-d '${JSON.stringify(body)}'`); } curlParts.push(`"${url}"`); examples.push({ title: "cURL Command", content: curlParts.join(' \\\n ') }); // JavaScript Fetch const fetchOptions: any = { method, headers }; if (body) { fetchOptions.body = JSON.stringify(body); } const jsCode = ` // PowerPages WebAPI ${params.operation} operation fetch('${url}', ${JSON.stringify(fetchOptions, null, 2)}) .then(response => { if (!response.ok) { throw new Error(\`HTTP error! status: \${response.status}\`); } return response.json(); }) .then(data => { console.log('Success:', data); }) .catch(error => { console.error('Error:', error); });`; examples.push({ title: "JavaScript (Fetch API)", content: jsCode.trim() }); // React Component Example const reactCode = ` import React, { useState, useEffect } from 'react'; const ${params.operation.charAt(0).toUpperCase() + params.operation.slice(1)}Component = () => { const [data, setData] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const perform${params.operation.charAt(0).toUpperCase() + params.operation.slice(1)} = async () => { setLoading(true); setError(null); try { const response = await fetch('${url}', ${JSON.stringify(fetchOptions, null, 6)}); if (!response.ok) { throw new Error(\`HTTP error! status: \${response.status}\`); } const result = await response.json(); setData(result); } catch (err) { setError(err.message); } finally { setLoading(false); } }; ${params.operation === 'retrieveMultiple' || params.operation === 'retrieve' ? ` useEffect(() => { perform${params.operation.charAt(0).toUpperCase() + params.operation.slice(1)}(); }, []);` : ''} return ( <div> <h3>${params.operation.charAt(0).toUpperCase() + params.operation.slice(1)} ${params.logicalEntityName || 'Entity'}</h3> ${params.operation !== 'retrieveMultiple' && params.operation !== 'retrieve' ? ` <button onClick={perform${params.operation.charAt(0).toUpperCase() + params.operation.slice(1)}} disabled={loading}> {loading ? 'Processing...' : '${params.operation.charAt(0).toUpperCase() + params.operation.slice(1)}'} </button>` : ''} {loading && <p>Loading...</p>} {error && <p style={{color: 'red'}}>Error: {error}</p>} {data && ( <div> <h4>Result:</h4> <pre>{JSON.stringify(data, null, 2)}</pre> </div> )} </div> ); }; export default ${params.operation.charAt(0).toUpperCase() + params.operation.slice(1)}Component;`; examples.push({ title: "React Component", content: reactCode.trim() }); // Add @odata.bind examples if present in the body if (body && hasODataBindProperties(body)) { const bindExamples = extractNavigationPropertyExamples(body); if (bindExamples.length > 0) { const odataBindInfo = ` ## @odata.bind Relationship Examples The request body includes relationship associations using @odata.bind: \`\`\`javascript ${bindExamples.join('\n')} \`\`\` ### @odata.bind Usage Patterns: 1. **Associate with existing record:** \`"navigationProperty@odata.bind": "/_api/entityset(guid)"\` 2. **Disassociate relationship:** \`"navigationProperty@odata.bind": null\` 3. **PowerPages URL Format:** - Use relative paths: \`/_api/contacts(guid)\` - Entity set names are typically plural: \`contacts\`, \`accounts\`, etc. ### Navigation Property Names: ${entityInfo && entityInfo.lookupNavMap.size > 0 ? Array.from(entityInfo.lookupNavMap.entries()) .map((entry: any) => { const [attr, nav] = entry as [string, string]; return `- Lookup attribute \`${attr}\` → Navigation property \`${nav}\``; }) .join('\n') : '- Navigation properties are automatically resolved from table schema'}`; examples.push({ title: "@odata.bind Relationships", content: odataBindInfo.trim() }); } } // Authentication context if requested if (params.includeAuthContext) { const authInfo = ` ## Authentication Context for PowerPages PowerPages uses different authentication mechanisms: 1. **Anonymous Access**: No authentication required for public data 2. **Authenticated Users**: Session-based authentication via portal login 3. **Request Verification Token**: Anti-CSRF protection for state-changing operations ### Getting Request Verification Token (JavaScript): \`\`\`javascript // Get the token from the page (usually in a hidden input or meta tag) const token = document.querySelector('input[name="__RequestVerificationToken"]')?.value || document.querySelector('meta[name="__RequestVerificationToken"]')?.content; // Include in headers for POST/PATCH/DELETE operations headers['__RequestVerificationToken'] = token; \`\`\` ### User Context: \`\`\`javascript // Access current user information (if available) const userContext = { isAuthenticated: window.Shell?.user?.isAuthenticated || false, userId: window.Shell?.user?.id, userName: window.Shell?.user?.displayName }; \`\`\``; examples.push({ title: "Authentication Information", content: authInfo.trim() }); } // Add schema information if available if (entityInfo && entityInfo.logicalName) { const schemaInfo = ` ## Schema Information **Entity:** ${entityInfo.logicalName} (${entityInfo.entitySetName}) **Primary ID:** ${entityInfo.primaryIdAttribute || 'Not available'} **Primary Name:** ${entityInfo.primaryNameAttribute || 'Not available'} ### Available Fields: ${entityInfo.attributes && entityInfo.attributes.length > 0 ? entityInfo.attributes .filter((attr: any) => attr?.LogicalName) .slice(0, 10) // Show first 10 fields .map((attr: any) => `- \`${attr.LogicalName}\` (${attr.AttributeType})`) .join('\n') + (entityInfo.attributes.length > 10 ? `\n- ... and ${entityInfo.attributes.length - 10} more fields` : '') : 'Schema information not available'} ### Lookup Navigation Properties: ${entityInfo.lookupNavMap && entityInfo.lookupNavMap.size > 0 ? Array.from(entityInfo.lookupNavMap.entries()) .map((entry: any) => { const [attr, nav] = entry as [string, string]; return `- \`${attr}\` → \`${nav}\``; }) .join('\n') : 'No lookup relationships found'}`; examples.push({ title: "Entity Schema", content: schemaInfo.trim() }); } const result = examples.map(example => `## ${example.title}\n\n\`\`\`${example.title.includes('React') ? 'jsx' : example.title.includes('JavaScript') ? 'javascript' : example.title.includes('cURL') ? 'bash' : example.title.includes('@odata.bind') || example.title.includes('Schema') || example.title.includes('Authentication') ? 'markdown' : 'http'}\n${example.content}\n\`\`\`` ).join('\n\n'); return { content: [ { type: "text", text: result } ] }; } catch (error) { return { content: [ { type: "text", text: `Error generating PowerPages WebAPI call: ${error instanceof Error ? error.message : 'Unknown error'}` } ], isError: true }; } } ); }
  • src/index.ts:234-234 (registration)
    Invocation of the registration factory function to register the tool on the MCP server instance.
    generatePowerPagesWebAPICallTool(server, dataverseClient);
  • Key helper: Resolves entity metadata (logical/set name, primaries, attributes, lookup nav map) for schema-aware features like sample generation and @odata.bind correction.
    async function resolvePowerPagesEntityInfo(client: DataverseClient, nameOrSet?: string): Promise<{ logicalName: string; entitySetName: string; primaryIdAttribute: string; primaryNameAttribute?: string; attributes: any[]; lookupNavMap: Map<string, string>; }> { if (!nameOrSet) { return { logicalName: '', entitySetName: '', primaryIdAttribute: '', primaryNameAttribute: undefined, attributes: [], lookupNavMap: new Map() }; } // Try to get by LogicalName directly with robust fallback const tryGetByLogicalName = async (ln: string) => { try { return await client.getMetadata( `EntityDefinitions(LogicalName='${ln}')?$select=EntitySetName,PrimaryIdAttribute,PrimaryNameAttribute,LogicalName` ); } catch { // Fallback without $select for environments that don't support it on singletons return await client.getMetadata( `EntityDefinitions(LogicalName='${ln}')` ); } }; // Try to get by EntitySetName (fallback) const tryGetByEntitySetName = async (esn: string) => { const resp = await client.getMetadata( `EntityDefinitions?$select=EntitySetName,LogicalName,PrimaryIdAttribute,PrimaryNameAttribute&$filter=EntitySetName eq '${esn}'` ); return resp?.value?.[0]; }; let def: any | null = null; try { def = await tryGetByLogicalName(nameOrSet); } catch { // If endsWith 's', try trimming 's' as a heuristic for logical name if (nameOrSet.endsWith('s')) { try { def = await tryGetByLogicalName(nameOrSet.slice(0, -1)); } catch { // ignore } } } if (!def) { try { def = await tryGetByEntitySetName(nameOrSet); } catch { // ignore } } if (!def) { // Last resort: return minimal info using naive pluralization return { logicalName: nameOrSet, entitySetName: nameOrSet.endsWith('s') ? nameOrSet : `${nameOrSet}s`, primaryIdAttribute: '', primaryNameAttribute: undefined, attributes: [], lookupNavMap: new Map() }; } const logicalName: string = def.LogicalName; const entitySetName: string = def.EntitySetName; // Fetch attributes (robust with fallback: try $select, then full set) let attributes: any[] = []; try { const attrsResp = await client.getMetadata( `EntityDefinitions(LogicalName='${logicalName}')/Attributes?$select=LogicalName,AttributeType,IsValidForCreate,IsValidForUpdate,IsPrimaryId,IsPrimaryName,RequiredLevel,Targets` ); attributes = attrsResp?.value || []; } catch { try { const attrsRespFull = await client.getMetadata( `EntityDefinitions(LogicalName='${logicalName}')/Attributes` ); attributes = attrsRespFull?.value || []; } catch { attributes = []; } } // Build lookup navigation property map const navMap: Map<string, string> = new Map(); try { const relResp = await client.getMetadata( `EntityDefinitions(LogicalName='${logicalName}')/ManyToOneRelationships?$select=ReferencingAttribute,ReferencingEntityNavigationPropertyName` ); for (const rel of relResp?.value || []) { if (rel?.ReferencingAttribute && rel?.ReferencingEntityNavigationPropertyName) { navMap.set(rel.ReferencingAttribute, rel.ReferencingEntityNavigationPropertyName); } } } catch { // ignore nav map errors } return { logicalName, entitySetName, primaryIdAttribute: def.PrimaryIdAttribute, primaryNameAttribute: def.PrimaryNameAttribute, attributes, lookupNavMap: navMap }; }

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mwhesse/mcp-dataverse'

If you have feedback or need assistance with the MCP directory API, please join our Discord server