Skip to main content
Glama

create_form

Create Tally forms by converting simple field definitions into structured blocks automatically. Specify title, fields, and optional status to generate forms for surveys, feedback, or data collection.

Instructions

Create a new Tally form with specified fields and configuration. This tool converts simple field definitions into Tally's complex blocks-based structure automatically. The status field is optional and defaults to DRAFT if not specified.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
titleYesForm title (required) - will be displayed as the main form heading
descriptionNoOptional form description - displayed below the title to provide context
statusNoForm publication status. Use DRAFT for unpublished forms that are being worked on, or PUBLISHED for live forms. Defaults to DRAFT if not specified.DRAFT
fieldsYesArray of form fields/questions. Each field will be converted to appropriate Tally blocks automatically.

Implementation Reference

  • The main execution handler for form creation. Processes input arguments to generate a FormConfig (via direct config, template, or NLP), calls TallyApiService.createForm to create the form, extracts form URL/ID, and generates enriched field configurations using block builder utilities.
    public async execute(args: FormCreationArgs): Promise<FormCreationResult> {
      console.log(`Executing form creation tool with args: ${JSON.stringify(args)}`);
    
      let formConfig: FormConfig | undefined;
    
      // 1. Highest priority: direct FormConfig
      if (args.formConfig) {
        formConfig = { ...args.formConfig };
        if (args.formTitle) {
          formConfig.title = args.formTitle;
        }
      }
    
      // 2. Template instantiation (if no direct config)
      if (!formConfig && args.templateId) {
        formConfig = this.templateService.instantiateTemplate(args.templateId, args.formTitle);
        if (!formConfig) {
          throw new Error(`Template with ID '${args.templateId}' not found.`);
        }
      }
    
      // 3. Natural-language prompt (if neither of the above)
      if (!formConfig && args.naturalLanguagePrompt) {
        formConfig = this.nlpService.generateFormConfig(args.naturalLanguagePrompt);
        if (args.formTitle) {
          formConfig.title = args.formTitle;
        }
      }
    
      if (!formConfig) {
        throw new Error('One of formConfig, naturalLanguagePrompt, or templateId must be provided.');
      }
    
      let createdTallyForm;
      try {
        createdTallyForm = await this.tallyApiService.createForm(formConfig);
      } catch (err: any) {
        console.error('[FormCreationTool] createForm error', err?.response?.data || err?.message || err);
        throw err;
      }
    
      console.log('[FormCreationTool] Created form response:', JSON.stringify(createdTallyForm, null, 2));
    
      // Try to capture any possible share link fields
      let formUrl: string | undefined =
        (createdTallyForm as any).url ||
        (createdTallyForm as any).shareUrl ||
        (createdTallyForm as any).share_url ||
        (createdTallyForm as any).publicUrl;
    
      if (!formUrl && createdTallyForm.id) {
        formUrl = `https://tally.so/forms/${createdTallyForm.id}/edit`;
      }
    
      // Generate enriched field configurations using enhanced BlockBuilder
      const blockResult = buildBlocksForFormWithMapping(formConfig);
      const enrichedFieldConfigurations = generateEnrichedFieldConfigurations(formConfig, blockResult.fieldBlockMapping);
    
      return {
        formUrl,
        formId: createdTallyForm.id,
        formConfig,
        generatedFieldIds: blockResult.fieldIds,
        enrichedFieldConfigurations,
      };
    }
  • JSON Schema definition for the 'create_form' tool input parameters, including title, optional description/status, and detailed fields array with supported types, used in MCP tools/list response.
    {
      name: 'create_form',
      description: 'Create a new Tally form with specified fields and configuration. This tool converts simple field definitions into Tally\'s complex blocks-based structure automatically. The form status defaults to DRAFT if not specified.',
      inputSchema: {
        type: 'object',
        properties: {
          title: { 
            type: 'string', 
            description: 'Form title (required) - will be displayed as the main form heading',
            minLength: 1,
            maxLength: 100
          },
          description: { 
            type: 'string', 
            description: 'Optional form description - displayed below the title to provide context' 
          },
          status: {
            type: 'string',
            enum: ['DRAFT', 'PUBLISHED'],
            description: 'Form publication status. Use DRAFT for unpublished forms that are being worked on, or PUBLISHED for live forms. Defaults to DRAFT if not specified.',
            default: 'DRAFT'
          },
          fields: {
            type: 'array',
            description: 'Array of form fields/questions. Each field will be converted to appropriate Tally blocks automatically.',
            minItems: 1,
            items: {
              type: 'object',
              properties: {
                type: { 
                  type: 'string', 
                  enum: ['text', 'email', 'number', 'textarea', 'select', 'checkbox', 'radio'],
                  description: 'Field input type. Maps to Tally blocks: text→INPUT_TEXT, email→INPUT_EMAIL, number→INPUT_NUMBER, textarea→TEXTAREA, select→DROPDOWN, checkbox→CHECKBOXES, radio→MULTIPLE_CHOICE'
                },
                label: { 
                  type: 'string',
                  description: 'Field label/question text - what the user will see',
                  minLength: 1
                },
                required: { 
                  type: 'boolean',
                  description: 'Whether this field must be filled out before form submission',
                  default: false
                },
                options: { 
                  type: 'array', 
                  items: { type: 'string' },
                  description: 'Available options for select, checkbox, or radio field types. Required for select/checkbox/radio fields.'
                }
              },
              required: ['type', 'label'],
              additionalProperties: false
            }
          }
        },
        required: ['title', 'fields'],
        additionalProperties: false,
        examples: [
          {
            title: "Customer Feedback Survey",
            description: "Help us improve our service",
            status: "DRAFT",
            fields: [
              {
                type: "text",
                label: "What is your name?",
                required: true
              },
              {
                type: "email", 
                label: "Email address",
                required: true
              },
              {
                type: "select",
                label: "How would you rate our service?",
                required: false,
                options: ["Excellent", "Good", "Fair", "Poor"]
              }
            ]
          }
        ]
      }
  • Initializes and registers the FormCreationTool instance as 'form_creation' in the server's tools object, mapping it to handle 'create_form' tool calls (though switch case pending).
    private initializeTools(): void {
      this.log('info', 'Initializing tools...');
      const apiClientConfig: TallyApiClientConfig = { accessToken: env.TALLY_API_KEY };
      const tallyApiClient = new TallyApiClient(apiClientConfig);
      this.tools = {
        workspaceManagement: new WorkspaceManagementTool(apiClientConfig),
        template: new TemplateTool(),
        form_creation: new FormCreationTool(apiClientConfig),
        form_modification: new FormModificationTool(apiClientConfig),
        form_retrieval: new FormRetrievalTool(apiClientConfig),
        form_sharing: new FormSharingTool(tallyApiClient),
        form_permissions: new FormPermissionManager(apiClientConfig),
        submission_analysis: new SubmissionAnalysisTool(apiClientConfig),
        diagnostic: new DiagnosticTool(),
      };
      this.log('info', 'Tools initialized.');
    }
  • Core helper method that performs the actual HTTP request to Tally API to create a form from FormConfig, called by FormCreationTool.execute.
    public async createForm(formConfig: FormConfig): Promise<TallyForm> {
      // The Tally API endpoint for creating forms
      const endpoint = '/forms';
    
      // Map our FormConfig to the payload expected by the Tally API
      const payload = this.mapFormConfigToPayload(formConfig);
    
      // Validate the payload before sending to ensure it meets Tally API requirements
      const validatedPayload = TallyFormCreatePayloadSchema.parse(payload);
    
      return this.apiClient.requestWithValidation('POST', endpoint, TallyFormSchema, validatedPayload);
    }
    
    /**
     * Retrieves a specific form by its ID from Tally.
     * @param formId The ID of the form to retrieve.
     * @returns The Tally form data.
     */
    public async getForm(formId: string): Promise<TallyForm> {
      return this.apiClient.getForm(formId);
    }
    
    /**
     * Retrieves a list of forms from Tally.
     * @param options Options for filtering and pagination.
     * @returns The list of forms response.
     */
    public async getForms(options: {
      page?: number;
      limit?: number;
      workspaceId?: string;
    } = {}): Promise<TallyFormsResponse> {
      return this.apiClient.getForms(options);
    }
    
    /**
     * Updates an existing form in Tally.
     * @param formId The ID of the form to update.
     * @param formConfig The updated configuration of the form.
     * @returns The updated Tally form.
     */
    public async updateForm(formId: string, formConfig: Partial<FormConfig>): Promise<TallyForm> {
      const endpoint = `/forms/${formId}`;
      
      // Map the FormConfig to the payload expected by the Tally API
      const payload = this.mapToTallyUpdatePayload(formConfig);
    
      // Validate the payload before sending to ensure it meets Tally API requirements
      const validatedPayload = TallyFormUpdatePayloadSchema.parse(payload);
    
      return this.apiClient.requestWithValidation('PUT', endpoint, TallyFormSchema, validatedPayload);
    }
    
    /**
     * Partially updates an existing form in Tally.
     * @param formId The ID of the form to update.
     * @param updates Partial updates to apply to the form.
     * @returns The updated Tally form.
     */
    public async patchForm(formId: string, updates: Record<string, any>): Promise<TallyForm> {
      const endpoint = `/forms/${formId}`;
      
      return this.apiClient.requestWithValidation('PATCH', endpoint, TallyFormSchema, updates);
    }
    
    /**
     * Maps our internal FormConfig to the payload expected by the Tally API.
     * This is a placeholder and will be implemented in detail later.
     * @param formConfig The internal form configuration.
     * @returns The payload for the Tally API.
     */
    private mapFormConfigToPayload(formConfig: FormConfig): TallyFormCreatePayload {
      // Build Tally-compatible blocks using our utility
      const blocks = buildBlocksForForm(formConfig);
    
      const payload = {
        status: 'PUBLISHED' as const,
        name: formConfig.title,
        blocks: blocks as any, // Cast to any to resolve type compatibility
      };
    
      // DEBUG: log payload for development
      if (process.env.DEBUG_TALLY_PAYLOAD === '1') {
  • The FormCreationTool class definition, implementing the Tool interface, with dependencies injected for NLP, Tally API, and templates, serving as the core handler for create_form operations.
    export class FormCreationTool implements Tool<FormCreationArgs, FormCreationResult> {
      public readonly name = 'form_creation_tool';
      public readonly description = 'Creates a Tally form from a natural language description or a template.';
    
      private nlpService: NlpService;
      private tallyApiService: TallyApiService;
      private templateService: TemplateService;
    
      constructor(apiClientConfig: TallyApiClientConfig) {
        this.nlpService = new NlpService();
        this.tallyApiService = new TallyApiService(apiClientConfig);
        this.templateService = new TemplateService();
      }
    
      public async execute(args: FormCreationArgs): Promise<FormCreationResult> {
        console.log(`Executing form creation tool with args: ${JSON.stringify(args)}`);
    
        let formConfig: FormConfig | undefined;
    
        // 1. Highest priority: direct FormConfig
        if (args.formConfig) {
          formConfig = { ...args.formConfig };
          if (args.formTitle) {
            formConfig.title = args.formTitle;
          }
        }
    
        // 2. Template instantiation (if no direct config)
        if (!formConfig && args.templateId) {
          formConfig = this.templateService.instantiateTemplate(args.templateId, args.formTitle);
          if (!formConfig) {
            throw new Error(`Template with ID '${args.templateId}' not found.`);
          }
        }
    
        // 3. Natural-language prompt (if neither of the above)
        if (!formConfig && args.naturalLanguagePrompt) {
          formConfig = this.nlpService.generateFormConfig(args.naturalLanguagePrompt);
          if (args.formTitle) {
            formConfig.title = args.formTitle;
          }
        }
    
        if (!formConfig) {
          throw new Error('One of formConfig, naturalLanguagePrompt, or templateId must be provided.');
        }
    
        let createdTallyForm;
        try {
          createdTallyForm = await this.tallyApiService.createForm(formConfig);
        } catch (err: any) {
          console.error('[FormCreationTool] createForm error', err?.response?.data || err?.message || err);
          throw err;
        }
    
        console.log('[FormCreationTool] Created form response:', JSON.stringify(createdTallyForm, null, 2));
    
        // Try to capture any possible share link fields
        let formUrl: string | undefined =
          (createdTallyForm as any).url ||
          (createdTallyForm as any).shareUrl ||
          (createdTallyForm as any).share_url ||
          (createdTallyForm as any).publicUrl;
    
        if (!formUrl && createdTallyForm.id) {
          formUrl = `https://tally.so/forms/${createdTallyForm.id}/edit`;
        }
    
        // Generate enriched field configurations using enhanced BlockBuilder
        const blockResult = buildBlocksForFormWithMapping(formConfig);
        const enrichedFieldConfigurations = generateEnrichedFieldConfigurations(formConfig, blockResult.fieldBlockMapping);
    
        return {
          formUrl,
          formId: createdTallyForm.id,
          formConfig,
          generatedFieldIds: blockResult.fieldIds,
          enrichedFieldConfigurations,
        };
      }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It discloses that the tool performs creation (a write operation) and handles automatic conversion of field definitions, which is useful behavioral context. However, it doesn't mention permissions needed, rate limits, error conditions, or what the response looks like (e.g., returns a form ID), leaving gaps for a mutation tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized with two sentences: the first states the core purpose and transformation, and the second clarifies the status default. It's front-loaded with the main action and avoids unnecessary details, though it could be slightly more structured (e.g., explicitly listing key parameters).

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (4 parameters, mutation operation) and no annotations or output schema, the description is moderately complete. It covers the creation purpose and default behavior but lacks details on permissions, response format, error handling, or integration with sibling tools. For a creation tool with no structured safety hints, more context would be beneficial.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all parameters thoroughly. The description adds minimal value by noting the status field is optional and defaults to DRAFT, but doesn't provide additional semantics beyond what the schema specifies (e.g., it doesn't explain field interactions or constraints not in the schema). This meets the baseline for high schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the specific action ('Create a new Tally form') and resource ('with specified fields and configuration'), distinguishing it from sibling tools like modify_form or delete_form. It also explains the transformation process ('converts simple field definitions into Tally's complex blocks-based structure automatically'), which adds unique value beyond just the tool name.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage by mentioning the status field defaults to DRAFT, suggesting this is for initial form creation. However, it doesn't explicitly state when to use this tool versus alternatives like modify_form for updates, or provide guidance on prerequisites or constraints. The context is clear but lacks explicit alternatives or exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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/learnwithcc/tally-mcp'

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