Skip to main content
Glama

analyze_submissions

Analyze Tally form submissions to extract insights on trends, responses, summaries, and completion rates for data-driven decision making.

Instructions

Analyze form submissions and provide insights

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
formIdYesID of the form to analyze
analysisTypeYesType of analysis to perform

Implementation Reference

  • JSON Schema definition for the analyze_submissions tool input, including formId, analysisType, and optional dateRange.
      name: 'analyze_submissions',
      description: 'Analyze submission data for a Tally form to provide insights and statistics',
      inputSchema: {
        type: 'object',
        properties: {
          formId: { type: 'string', description: 'ID of the form to analyze submissions for' },
          analysisType: { 
            type: 'string', 
            enum: ['basic_stats', 'response_patterns', 'completion_rates', 'field_analysis'],
            description: 'Type of analysis to perform'
          },
          dateRange: {
            type: 'object',
            properties: {
              start: { type: 'string', description: 'Start date for analysis (ISO 8601)' },
              end: { type: 'string', description: 'End date for analysis (ISO 8601)' }
            }
          }
        },
        required: ['formId', 'analysisType']
      }
    },
  • Primary handler function that fetches submissions and computes basic analysis metrics: total submissions and completion rate.
    public async analyze(formId: string, filters: SubmissionFilters = {}): Promise<SubmissionAnalysis> {
      const response = await this.submissionService.getFormSubmissions(formId, filters);
      
      const totalSubmissions = response.totalNumberOfSubmissionsPerFilter.all;
      const completedSubmissions = response.totalNumberOfSubmissionsPerFilter.completed;
    
      const completionRate = totalSubmissions > 0 ? (completedSubmissions / totalSubmissions) * 100 : 0;
    
      return {
        totalSubmissions,
        completionRate,
      };
    }
  • Server tool registry where SubmissionAnalysisTool instance is created (as 'submission_analysis'), providing the underlying implementation for submission analysis tools.
    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.');
  • Export statement registering SubmissionAnalysisTool for use in the MCP server.
    export { SubmissionAnalysisTool } from './submission-tool';
  • Main class containing all submission analysis logic, including additional helper methods for listing, filtering, exporting, and advanced analytics.
    export class SubmissionAnalysisTool {
      private submissionService: SubmissionService;
    
      constructor(apiClientConfig: TallyApiClientConfig = {}) {
        this.submissionService = new SubmissionService(apiClientConfig);
      }
    
      public async analyze(formId: string, filters: SubmissionFilters = {}): Promise<SubmissionAnalysis> {
        const response = await this.submissionService.getFormSubmissions(formId, filters);
        
        const totalSubmissions = response.totalNumberOfSubmissionsPerFilter.all;
        const completedSubmissions = response.totalNumberOfSubmissionsPerFilter.completed;
    
        const completionRate = totalSubmissions > 0 ? (completedSubmissions / totalSubmissions) * 100 : 0;
    
        return {
          totalSubmissions,
          completionRate,
        };
      }
    
      public async list(formId: string, filters: SubmissionFilters = {}): Promise<TallySubmission[]> {
        const response = await this.submissionService.getFormSubmissions(formId, filters);
        return response.submissions;
      }
    
      public async filterByDateRange(formId: string, startDate: string, endDate: string): Promise<TallySubmission[]> {
        return this.list(formId, { startDate, endDate });
      }
    
      public async filterByStatus(formId: string, status: SubmissionStatusFilter): Promise<TallySubmission[]> {
        return this.list(formId, { status });
      }
    
      public async formatForAnalysis(formId: string, filters: SubmissionFilters = {}): Promise<FormattedSubmission[]> {
        const response = await this.submissionService.getFormSubmissions(formId, filters);
        const questions = response.questions;
        const submissions = response.submissions;
    
        const questionMap = new Map<string, string>(questions.map(q => [q.id, q.title]));
    
        return submissions.map(sub => ({
          submissionId: sub.id,
          submittedAt: sub.submittedAt,
          isCompleted: sub.isCompleted,
          responses: sub.responses.map(res => ({
            questionTitle: questionMap.get(res.questionId) || 'Unknown Question',
            answer: res.value,
          })),
        }));
      }
    
      public async getAverageRating(formId: string, questionTitle: string, filters: SubmissionFilters = {}): Promise<number | null> {
        const formattedSubmissions = await this.formatForAnalysis(formId, filters);
        let totalRating = 0;
        let ratingCount = 0;
    
        for (const sub of formattedSubmissions) {
          for (const res of sub.responses) {
            if (res.questionTitle === questionTitle && typeof res.answer === 'number') {
              totalRating += res.answer;
              ratingCount++;
            }
          }
        }
    
        return ratingCount > 0 ? totalRating / ratingCount : null;
      }
    
      public async getResponseDistribution(formId: string, questionTitle: string, filters: SubmissionFilters = {}): Promise<Record<string, number>> {
        const formattedSubmissions = await this.formatForAnalysis(formId, filters);
        const distribution: Record<string, number> = {};
    
        for (const sub of formattedSubmissions) {
          for (const res of sub.responses) {
            if (res.questionTitle === questionTitle) {
              const answer = String(res.answer);
              distribution[answer] = (distribution[answer] || 0) + 1;
            }
          }
        }
    
        return distribution;
      }
    
      public async exportToCSV(formId: string, filePath: string, filters: SubmissionFilters = {}): Promise<void> {
        const formattedSubmissions = await this.formatForAnalysis(formId, filters);
        if (formattedSubmissions.length === 0) {
          return;
        }
    
        // Flatten the data for CSV
        const flattenedData = formattedSubmissions.flatMap(sub => 
          sub.responses.map(res => ({
            submissionId: sub.submissionId,
            submittedAt: sub.submittedAt,
            isCompleted: sub.isCompleted,
            questionTitle: res.questionTitle,
            answer: res.answer
          }))
        );
    
        const csv = Papa.unparse(flattenedData);
        
        // Ensure the directory exists
        const dir = path.dirname(filePath);
        if (!fs.existsSync(dir)) {
          fs.mkdirSync(dir, { recursive: true });
        }
        
        fs.writeFileSync(filePath, csv);
      }
    
      public async exportToJSON(formId: string, filePath: string, filters: SubmissionFilters = {}): Promise<void> {
        const formattedSubmissions = await this.formatForAnalysis(formId, filters);
        
        // Ensure the directory exists
        const dir = path.dirname(filePath);
        if (!fs.existsSync(dir)) {
          fs.mkdirSync(dir, { recursive: true });
        }
    
        fs.writeFileSync(filePath, JSON.stringify(formattedSubmissions, null, 2));
      }
    
      public async search(formId: string, query: string, filters: SubmissionFilters = {}): Promise<FormattedSubmission[]> {
        const formattedSubmissions = await this.formatForAnalysis(formId, filters);
        const lowerCaseQuery = query.toLowerCase();
    
        return formattedSubmissions.filter(sub => 
          sub.responses.some(res => 
            String(res.answer).toLowerCase().includes(lowerCaseQuery)
          )
        );
      }
    } 

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