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}`;
    }
Install Server

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