Skip to main content
Glama
mwhesse

Dataverse MCP Server

by mwhesse

create_dataverse_column

Add custom fields to Dataverse tables with specific data types including text, numbers, dates, lookups, and choice lists to store structured data.

Instructions

Creates a new column (field) in a Dataverse table with the specified data type and configuration. Supports various column types including text, numbers, dates, lookups, and choice lists. Use this to add new fields to store specific data in your tables. Requires a solution context to be set first.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
columnTypeYesType of the column
dateTimeFormatNoFormat for datetime columns
defaultValueNoDefault value for the column
descriptionNoDescription of the column
displayNameYesDisplay name for the column (e.g., 'Customer Email')
entityLogicalNameYesLogical name of the table to add the column to
falseOptionLabelNoLabel for false option in boolean columns (default: 'No')
formatNoFormat for string columns
isAuditEnabledNoWhether auditing is enabled for this column
isValidForAdvancedFindNoWhether the column appears in Advanced Find
isValidForCreateNoWhether the column can be set during create
isValidForUpdateNoWhether the column can be updated
maxLengthNoMaximum length for string columns (default: 100)
maxValueNoMaximum value for integer/decimal columns
minValueNoMinimum value for integer/decimal columns
optionSetNameNoName of the option set for picklist columns
optionsNoOptions for picklist columns
precisionNoPrecision for decimal columns (default: 2)
requiredLevelNoRequired level of the columnNone
targetEntityNoTarget entity for lookup columns
trueOptionLabelNoLabel for true option in boolean columns (default: 'Yes')

Implementation Reference

  • The asynchronous handler function that implements the core logic for creating a Dataverse column. It generates logical/schema names, constructs attribute metadata based on the specified columnType (e.g., String, Picklist, Lookup), handles type-specific properties, and posts the metadata to the EntityDefinitions/Attributes endpoint.
    async (params) => { try { // Get the customization prefix from the solution context const prefix = client.getCustomizationPrefix(); if (!prefix) { throw new Error('No customization prefix available. Please set a solution context using set_solution_context tool first.'); } // Generate the logical name and schema name const logicalName = generateColumnLogicalName(params.displayName, prefix); const schemaName = generateColumnSchemaName(params.displayName, prefix); let attributeDefinition: any = { LogicalName: logicalName, SchemaName: schemaName, DisplayName: createLocalizedLabel(params.displayName), Description: params.description ? createLocalizedLabel(params.description) : undefined, RequiredLevel: { Value: params.requiredLevel, CanBeChanged: true, ManagedPropertyLogicalName: "canmodifyrequirementlevelsettings" }, IsCustomAttribute: true }; // Set type-specific properties switch (params.columnType) { case "String": attributeDefinition["@odata.type"] = "Microsoft.Dynamics.CRM.StringAttributeMetadata"; attributeDefinition.MaxLength = params.maxLength || 100; // Remove Format property for now to avoid enum issues if (params.defaultValue && typeof params.defaultValue === "string") { attributeDefinition.DefaultValue = params.defaultValue; } break; case "Integer": attributeDefinition["@odata.type"] = "Microsoft.Dynamics.CRM.IntegerAttributeMetadata"; // Remove Format property for now to avoid enum issues if (params.minValue !== undefined) attributeDefinition.MinValue = params.minValue; if (params.maxValue !== undefined) attributeDefinition.MaxValue = params.maxValue; // Note: IntegerAttributeMetadata doesn't support DefaultValue property break; case "Decimal": attributeDefinition["@odata.type"] = "Microsoft.Dynamics.CRM.DecimalAttributeMetadata"; attributeDefinition.Precision = params.precision || 2; if (params.minValue !== undefined) attributeDefinition.MinValue = params.minValue; if (params.maxValue !== undefined) attributeDefinition.MaxValue = params.maxValue; if (params.defaultValue && typeof params.defaultValue === "number") { attributeDefinition.DefaultValue = params.defaultValue; } break; case "Money": attributeDefinition["@odata.type"] = "Microsoft.Dynamics.CRM.MoneyAttributeMetadata"; attributeDefinition.Precision = params.precision || 2; if (params.minValue !== undefined) attributeDefinition.MinValue = params.minValue; if (params.maxValue !== undefined) attributeDefinition.MaxValue = params.maxValue; break; case "Boolean": attributeDefinition["@odata.type"] = "Microsoft.Dynamics.CRM.BooleanAttributeMetadata"; attributeDefinition.OptionSet = { "@odata.type": "Microsoft.Dynamics.CRM.BooleanOptionSetMetadata", TrueOption: { Value: 1, Label: createLocalizedLabel(params.trueOptionLabel || "Yes") }, FalseOption: { Value: 0, Label: createLocalizedLabel(params.falseOptionLabel || "No") } }; if (params.defaultValue && typeof params.defaultValue === "boolean") { attributeDefinition.DefaultValue = params.defaultValue; } break; case "DateTime": attributeDefinition["@odata.type"] = "Microsoft.Dynamics.CRM.DateTimeAttributeMetadata"; // Remove Format property for now to avoid enum issues attributeDefinition.DateTimeBehavior = { Value: "UserLocal" }; break; case "Memo": attributeDefinition["@odata.type"] = "Microsoft.Dynamics.CRM.MemoAttributeMetadata"; attributeDefinition.MaxLength = params.maxLength || 2000; break; case "Double": attributeDefinition["@odata.type"] = "Microsoft.Dynamics.CRM.DoubleAttributeMetadata"; if (params.minValue !== undefined) attributeDefinition.MinValue = params.minValue; if (params.maxValue !== undefined) attributeDefinition.MaxValue = params.maxValue; attributeDefinition.Precision = params.precision || 2; break; case "BigInt": attributeDefinition["@odata.type"] = "Microsoft.Dynamics.CRM.BigIntAttributeMetadata"; break; case "Lookup": if (!params.targetEntity) { throw new Error("targetEntity is required for Lookup columns"); } attributeDefinition["@odata.type"] = "Microsoft.Dynamics.CRM.LookupAttributeMetadata"; attributeDefinition.Targets = [params.targetEntity]; break; case "Picklist": attributeDefinition["@odata.type"] = "Microsoft.Dynamics.CRM.PicklistAttributeMetadata"; if (params.optionSetName) { // Reference an existing global option set using the MetadataId // First get the option set to retrieve its MetadataId try { const globalOptionSet = await client.getMetadata( `GlobalOptionSetDefinitions(Name='${params.optionSetName}')` ); attributeDefinition["GlobalOptionSet@odata.bind"] = `/GlobalOptionSetDefinitions(${globalOptionSet.MetadataId})`; } catch (error) { throw new Error(`Global option set '${params.optionSetName}' not found: ${error instanceof Error ? error.message : 'Unknown error'}`); } } else if (params.options && params.options.length > 0) { // Create a new local option set attributeDefinition.OptionSet = { "@odata.type": "Microsoft.Dynamics.CRM.OptionSetMetadata", Name: `${params.entityLogicalName}_${logicalName}`, DisplayName: createLocalizedLabel(`${params.displayName} Options`), IsGlobal: false, OptionSetType: "Picklist", // Use string value instead of numeric Options: params.options.map(option => ({ Value: option.value, Label: createLocalizedLabel(option.label), Description: option.description ? createLocalizedLabel(option.description) : undefined })) }; } else { throw new Error("Either optionSetName (for global option set) or options array (for local option set) is required for Picklist columns"); } break; default: throw new Error(`Unsupported column type: ${params.columnType}`); } const result = await client.postMetadata( `EntityDefinitions(LogicalName='${params.entityLogicalName}')/Attributes`, attributeDefinition ); return { content: [ { type: "text", text: `Successfully created column '${logicalName}' with display name '${params.displayName}' of type '${params.columnType}' in table '${params.entityLogicalName}'.\n\nGenerated names:\n- Logical Name: ${logicalName}\n- Schema Name: ${schemaName}\n\nResponse: ${JSON.stringify(result, null, 2)}` } ] }; } catch (error) { return { content: [ { type: "text", text: `Error creating column: ${error instanceof Error ? error.message : 'Unknown error'}` } ], isError: true }; } }
  • The inputSchema using Zod that validates and describes parameters for the tool, including required fields like entityLogicalName, displayName, columnType, and optional type-specific options like maxLength, options for picklists, etc.
    inputSchema: { entityLogicalName: z.string().describe("Logical name of the table to add the column to"), displayName: z.string().describe("Display name for the column (e.g., 'Customer Email')"), description: z.string().optional().describe("Description of the column"), columnType: z.enum([ "String", "Integer", "Decimal", "Money", "Boolean", "DateTime", "Picklist", "Lookup", "Memo", "Double", "BigInt" ]).describe("Type of the column"), requiredLevel: z.enum(["None", "SystemRequired", "ApplicationRequired", "Recommended"]).default("None").describe("Required level of the column"), isAuditEnabled: z.boolean().optional().describe("Whether auditing is enabled for this column"), isValidForAdvancedFind: z.boolean().optional().describe("Whether the column appears in Advanced Find"), isValidForCreate: z.boolean().optional().describe("Whether the column can be set during create"), isValidForUpdate: z.boolean().optional().describe("Whether the column can be updated"), // String-specific options maxLength: z.number().optional().describe("Maximum length for string columns (default: 100)"), format: z.enum(["Email", "Text", "TextArea", "Url", "Phone"]).optional().describe("Format for string columns"), // Integer-specific options minValue: z.number().optional().describe("Minimum value for integer/decimal columns"), maxValue: z.number().optional().describe("Maximum value for integer/decimal columns"), // Decimal-specific options precision: z.number().optional().describe("Precision for decimal columns (default: 2)"), // DateTime-specific options dateTimeFormat: z.enum(["DateOnly", "DateAndTime"]).optional().describe("Format for datetime columns"), // Boolean-specific options trueOptionLabel: z.string().optional().describe("Label for true option in boolean columns (default: 'Yes')"), falseOptionLabel: z.string().optional().describe("Label for false option in boolean columns (default: 'No')"), defaultValue: z.union([z.string(), z.number(), z.boolean()]).optional().describe("Default value for the column"), // Lookup-specific options targetEntity: z.string().optional().describe("Target entity for lookup columns"), // Picklist-specific options optionSetName: z.string().optional().describe("Name of the option set for picklist columns"), options: z.array(z.object({ value: z.number(), label: z.string(), description: z.string().optional() })).optional().describe("Options for picklist columns") }
  • The exported createColumnTool function that registers the 'create_dataverse_column' tool with the MCP server, providing title, description, inputSchema, and the handler.
    export function createColumnTool(server: McpServer, client: DataverseClient) { server.registerTool( "create_dataverse_column", { title: "Create Dataverse Column", description: "Creates a new column (field) in a Dataverse table with the specified data type and configuration. Supports various column types including text, numbers, dates, lookups, and choice lists. Use this to add new fields to store specific data in your tables. Requires a solution context to be set first.", inputSchema: { entityLogicalName: z.string().describe("Logical name of the table to add the column to"), displayName: z.string().describe("Display name for the column (e.g., 'Customer Email')"), description: z.string().optional().describe("Description of the column"), columnType: z.enum([ "String", "Integer", "Decimal", "Money", "Boolean", "DateTime", "Picklist", "Lookup", "Memo", "Double", "BigInt" ]).describe("Type of the column"), requiredLevel: z.enum(["None", "SystemRequired", "ApplicationRequired", "Recommended"]).default("None").describe("Required level of the column"), isAuditEnabled: z.boolean().optional().describe("Whether auditing is enabled for this column"), isValidForAdvancedFind: z.boolean().optional().describe("Whether the column appears in Advanced Find"), isValidForCreate: z.boolean().optional().describe("Whether the column can be set during create"), isValidForUpdate: z.boolean().optional().describe("Whether the column can be updated"), // String-specific options maxLength: z.number().optional().describe("Maximum length for string columns (default: 100)"), format: z.enum(["Email", "Text", "TextArea", "Url", "Phone"]).optional().describe("Format for string columns"), // Integer-specific options minValue: z.number().optional().describe("Minimum value for integer/decimal columns"), maxValue: z.number().optional().describe("Maximum value for integer/decimal columns"), // Decimal-specific options precision: z.number().optional().describe("Precision for decimal columns (default: 2)"), // DateTime-specific options dateTimeFormat: z.enum(["DateOnly", "DateAndTime"]).optional().describe("Format for datetime columns"), // Boolean-specific options trueOptionLabel: z.string().optional().describe("Label for true option in boolean columns (default: 'Yes')"), falseOptionLabel: z.string().optional().describe("Label for false option in boolean columns (default: 'No')"), defaultValue: z.union([z.string(), z.number(), z.boolean()]).optional().describe("Default value for the column"), // Lookup-specific options targetEntity: z.string().optional().describe("Target entity for lookup columns"), // Picklist-specific options optionSetName: z.string().optional().describe("Name of the option set for picklist columns"), options: z.array(z.object({ value: z.number(), label: z.string(), description: z.string().optional() })).optional().describe("Options for picklist columns") } }, async (params) => { try { // Get the customization prefix from the solution context const prefix = client.getCustomizationPrefix(); if (!prefix) { throw new Error('No customization prefix available. Please set a solution context using set_solution_context tool first.'); } // Generate the logical name and schema name const logicalName = generateColumnLogicalName(params.displayName, prefix); const schemaName = generateColumnSchemaName(params.displayName, prefix); let attributeDefinition: any = { LogicalName: logicalName, SchemaName: schemaName, DisplayName: createLocalizedLabel(params.displayName), Description: params.description ? createLocalizedLabel(params.description) : undefined, RequiredLevel: { Value: params.requiredLevel, CanBeChanged: true, ManagedPropertyLogicalName: "canmodifyrequirementlevelsettings" }, IsCustomAttribute: true }; // Set type-specific properties switch (params.columnType) { case "String": attributeDefinition["@odata.type"] = "Microsoft.Dynamics.CRM.StringAttributeMetadata"; attributeDefinition.MaxLength = params.maxLength || 100; // Remove Format property for now to avoid enum issues if (params.defaultValue && typeof params.defaultValue === "string") { attributeDefinition.DefaultValue = params.defaultValue; } break; case "Integer": attributeDefinition["@odata.type"] = "Microsoft.Dynamics.CRM.IntegerAttributeMetadata"; // Remove Format property for now to avoid enum issues if (params.minValue !== undefined) attributeDefinition.MinValue = params.minValue; if (params.maxValue !== undefined) attributeDefinition.MaxValue = params.maxValue; // Note: IntegerAttributeMetadata doesn't support DefaultValue property break; case "Decimal": attributeDefinition["@odata.type"] = "Microsoft.Dynamics.CRM.DecimalAttributeMetadata"; attributeDefinition.Precision = params.precision || 2; if (params.minValue !== undefined) attributeDefinition.MinValue = params.minValue; if (params.maxValue !== undefined) attributeDefinition.MaxValue = params.maxValue; if (params.defaultValue && typeof params.defaultValue === "number") { attributeDefinition.DefaultValue = params.defaultValue; } break; case "Money": attributeDefinition["@odata.type"] = "Microsoft.Dynamics.CRM.MoneyAttributeMetadata"; attributeDefinition.Precision = params.precision || 2; if (params.minValue !== undefined) attributeDefinition.MinValue = params.minValue; if (params.maxValue !== undefined) attributeDefinition.MaxValue = params.maxValue; break; case "Boolean": attributeDefinition["@odata.type"] = "Microsoft.Dynamics.CRM.BooleanAttributeMetadata"; attributeDefinition.OptionSet = { "@odata.type": "Microsoft.Dynamics.CRM.BooleanOptionSetMetadata", TrueOption: { Value: 1, Label: createLocalizedLabel(params.trueOptionLabel || "Yes") }, FalseOption: { Value: 0, Label: createLocalizedLabel(params.falseOptionLabel || "No") } }; if (params.defaultValue && typeof params.defaultValue === "boolean") { attributeDefinition.DefaultValue = params.defaultValue; } break; case "DateTime": attributeDefinition["@odata.type"] = "Microsoft.Dynamics.CRM.DateTimeAttributeMetadata"; // Remove Format property for now to avoid enum issues attributeDefinition.DateTimeBehavior = { Value: "UserLocal" }; break; case "Memo": attributeDefinition["@odata.type"] = "Microsoft.Dynamics.CRM.MemoAttributeMetadata"; attributeDefinition.MaxLength = params.maxLength || 2000; break; case "Double": attributeDefinition["@odata.type"] = "Microsoft.Dynamics.CRM.DoubleAttributeMetadata"; if (params.minValue !== undefined) attributeDefinition.MinValue = params.minValue; if (params.maxValue !== undefined) attributeDefinition.MaxValue = params.maxValue; attributeDefinition.Precision = params.precision || 2; break; case "BigInt": attributeDefinition["@odata.type"] = "Microsoft.Dynamics.CRM.BigIntAttributeMetadata"; break; case "Lookup": if (!params.targetEntity) { throw new Error("targetEntity is required for Lookup columns"); } attributeDefinition["@odata.type"] = "Microsoft.Dynamics.CRM.LookupAttributeMetadata"; attributeDefinition.Targets = [params.targetEntity]; break; case "Picklist": attributeDefinition["@odata.type"] = "Microsoft.Dynamics.CRM.PicklistAttributeMetadata"; if (params.optionSetName) { // Reference an existing global option set using the MetadataId // First get the option set to retrieve its MetadataId try { const globalOptionSet = await client.getMetadata( `GlobalOptionSetDefinitions(Name='${params.optionSetName}')` ); attributeDefinition["GlobalOptionSet@odata.bind"] = `/GlobalOptionSetDefinitions(${globalOptionSet.MetadataId})`; } catch (error) { throw new Error(`Global option set '${params.optionSetName}' not found: ${error instanceof Error ? error.message : 'Unknown error'}`); } } else if (params.options && params.options.length > 0) { // Create a new local option set attributeDefinition.OptionSet = { "@odata.type": "Microsoft.Dynamics.CRM.OptionSetMetadata", Name: `${params.entityLogicalName}_${logicalName}`, DisplayName: createLocalizedLabel(`${params.displayName} Options`), IsGlobal: false, OptionSetType: "Picklist", // Use string value instead of numeric Options: params.options.map(option => ({ Value: option.value, Label: createLocalizedLabel(option.label), Description: option.description ? createLocalizedLabel(option.description) : undefined })) }; } else { throw new Error("Either optionSetName (for global option set) or options array (for local option set) is required for Picklist columns"); } break; default: throw new Error(`Unsupported column type: ${params.columnType}`); } const result = await client.postMetadata( `EntityDefinitions(LogicalName='${params.entityLogicalName}')/Attributes`, attributeDefinition ); return { content: [ { type: "text", text: `Successfully created column '${logicalName}' with display name '${params.displayName}' of type '${params.columnType}' in table '${params.entityLogicalName}'.\n\nGenerated names:\n- Logical Name: ${logicalName}\n- Schema Name: ${schemaName}\n\nResponse: ${JSON.stringify(result, null, 2)}` } ] }; } catch (error) { return { content: [ { type: "text", text: `Error creating column: ${error instanceof Error ? error.message : 'Unknown error'}` } ], isError: true }; } } );
  • createLocalizedLabel helper: Creates standardized LocalizedLabel objects used for DisplayName, Description, and Option labels.
    function createLocalizedLabel(text: string, languageCode: number = 1033): LocalizedLabel { return { LocalizedLabels: [ { Label: text, LanguageCode: languageCode, IsManaged: false, MetadataId: "00000000-0000-0000-0000-000000000000" } ], UserLocalizedLabel: { Label: text, LanguageCode: languageCode, IsManaged: false, MetadataId: "00000000-0000-0000-0000-000000000000" } };
  • generateColumnLogicalName and generateColumnSchemaName helpers: Generate unique logical/schema names from displayName and solution prefix.
    function generateColumnLogicalName(displayName: string, prefix: string): string { // Convert display name to lowercase, remove spaces and special characters const cleanName = displayName.toLowerCase() .replace(/[^a-z0-9\s]/g, '') // Remove special characters except spaces .replace(/\s+/g, ''); // Remove all spaces return `${prefix}_${cleanName}`; } // Helper function to generate schema name from display name and prefix function generateColumnSchemaName(displayName: string, prefix: string): string { // Remove whitespaces and special characters, but preserve original case const cleanName = displayName.replace(/\s+/g, '').replace(/[^a-zA-Z0-9]/g, ''); return `${prefix}_${cleanName}`; }

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