Government Service Automation with ChatGPT Apps: Citizen Service Portal Guide

Government agencies face unique challenges in digital transformation: serving diverse populations, ensuring accessibility for all citizens, maintaining strict security compliance, and delivering services efficiently with limited budgets. ChatGPT apps offer a revolutionary solution—intelligent, conversational interfaces that make government services accessible 24/7 while meeting FedRAMP, Section 508, and WCAG AAA requirements.

This comprehensive guide provides production-ready code for building government ChatGPT applications that automate citizen services, permit applications, and information retrieval. You'll learn how to implement multilingual support, ensure accessibility compliance, and maintain the security standards required for government deployment.

Whether you're modernizing a DMV, building a permit application system, or creating a citizen information portal, this guide gives you the technical foundation to deliver accessible, secure, and efficient government services through ChatGPT.

What you'll build: A complete government service automation system with permit applications, document validation, multilingual support, accessibility compliance, and FedRAMP-grade security—all deployable to the ChatGPT App Store.

Understanding Government Digital Service Requirements

Government ChatGPT applications must balance three critical priorities: accessibility for all citizens, security compliance with federal standards, and operational efficiency.

Core Requirements for Government Apps

Accessibility mandates: Section 508 requires all federal electronic content to be accessible to people with disabilities. For ChatGPT apps, this means WCAG 2.1 Level AAA compliance, screen reader optimization, keyboard navigation support, and alternative text for all visual content.

Security compliance: FedRAMP (Federal Risk and Authorization Management Program) establishes security standards for cloud services used by government agencies. Your ChatGPT app must implement encryption at rest and in transit, comprehensive audit logging, role-based access control, and data sovereignty controls.

Multilingual support: Many government agencies serve diverse populations requiring services in multiple languages. The U.S. Census Bureau reports over 350 languages spoken in American homes—your app should support at minimum Spanish, Chinese, Vietnamese, Korean, and Tagalog in addition to English.

Service availability: Citizens expect 24/7 access to government information. ChatGPT apps deliver always-available service without the operational costs of staffing call centers around the clock.

For a complete understanding of ChatGPT app architecture and deployment strategies, see our Complete Guide to ChatGPT Applications.

Building a Citizen Service Portal

The foundation of government service automation is a citizen-facing portal that handles common requests through conversational interactions.

Multilingual Permit Application Tool

This MCP tool handles permit applications in multiple languages with automatic form validation:

// tools/permit-application.ts
import { z } from 'zod';
import type { MCPTool } from '@modelcontextprotocol/sdk';

const permitApplicationSchema = z.object({
  permitType: z.enum([
    'building_permit',
    'business_license',
    'special_event',
    'parking_permit',
    'tree_removal'
  ]),
  applicantInfo: z.object({
    name: z.string().min(1, 'Name required'),
    address: z.string().min(10, 'Complete address required'),
    phone: z.string().regex(/^\+?1?\d{10,14}$/, 'Valid phone number required'),
    email: z.string().email('Valid email required'),
    preferredLanguage: z.enum(['en', 'es', 'zh', 'vi', 'ko', 'tl'])
  }),
  propertyInfo: z.object({
    parcelNumber: z.string().optional(),
    address: z.string().min(10),
    zoning: z.string().optional()
  }),
  projectDetails: z.object({
    description: z.string().min(20, 'Detailed description required'),
    estimatedCost: z.number().min(0).optional(),
    startDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, 'ISO date required'),
    contractor: z.string().optional()
  }),
  documents: z.array(z.object({
    type: z.enum(['proof_of_ownership', 'site_plan', 'contractor_license', 'insurance', 'other']),
    url: z.string().url(),
    filename: z.string()
  })).min(1, 'At least one document required')
});

const translations = {
  en: {
    submitted: 'Application submitted successfully',
    applicationNumber: 'Application number',
    estimatedReview: 'Estimated review time',
    nextSteps: 'Next steps',
    checkStatus: 'Check status at'
  },
  es: {
    submitted: 'Solicitud enviada exitosamente',
    applicationNumber: 'Número de solicitud',
    estimatedReview: 'Tiempo estimado de revisión',
    nextSteps: 'Próximos pasos',
    checkStatus: 'Verificar estado en'
  },
  zh: {
    submitted: '申请已成功提交',
    applicationNumber: '申请编号',
    estimatedReview: '预计审核时间',
    nextSteps: '下一步',
    checkStatus: '在此查看状态'
  }
  // Add vi, ko, tl translations...
};

export const permitApplicationTool: MCPTool = {
  name: 'submit_permit_application',
  description: 'Submit a permit application with automatic validation and multilingual support',

  inputSchema: {
    type: 'object',
    properties: {
      permitType: {
        type: 'string',
        enum: permitApplicationSchema.shape.permitType.options,
        description: 'Type of permit being requested'
      },
      applicantInfo: {
        type: 'object',
        properties: {
          name: { type: 'string' },
          address: { type: 'string' },
          phone: { type: 'string' },
          email: { type: 'string' },
          preferredLanguage: {
            type: 'string',
            enum: ['en', 'es', 'zh', 'vi', 'ko', 'tl']
          }
        },
        required: ['name', 'address', 'phone', 'email', 'preferredLanguage']
      },
      propertyInfo: {
        type: 'object',
        properties: {
          parcelNumber: { type: 'string' },
          address: { type: 'string' },
          zoning: { type: 'string' }
        },
        required: ['address']
      },
      projectDetails: {
        type: 'object',
        properties: {
          description: { type: 'string' },
          estimatedCost: { type: 'number' },
          startDate: { type: 'string', format: 'date' },
          contractor: { type: 'string' }
        },
        required: ['description', 'startDate']
      },
      documents: {
        type: 'array',
        items: {
          type: 'object',
          properties: {
            type: { type: 'string' },
            url: { type: 'string', format: 'uri' },
            filename: { type: 'string' }
          },
          required: ['type', 'url', 'filename']
        },
        minItems: 1
      }
    },
    required: ['permitType', 'applicantInfo', 'propertyInfo', 'projectDetails', 'documents']
  },

  async execute(args: unknown) {
    // Validate input
    const validated = permitApplicationSchema.parse(args);
    const { preferredLanguage } = validated.applicantInfo;
    const t = translations[preferredLanguage] || translations.en;

    // Generate application number
    const applicationNumber = `${validated.permitType.toUpperCase()}-${Date.now()}-${Math.random().toString(36).substr(2, 6).toUpperCase()}`;

    // Calculate estimated review time based on permit type
    const reviewTimes = {
      building_permit: '15-20 business days',
      business_license: '5-7 business days',
      special_event: '10-14 business days',
      parking_permit: '3-5 business days',
      tree_removal: '7-10 business days'
    };

    // Submit to government system (mock)
    await submitToPermitSystem({
      applicationNumber,
      ...validated,
      submittedAt: new Date().toISOString(),
      status: 'pending_review',
      assignedReviewer: null
    });

    // Send confirmation email
    await sendConfirmationEmail({
      to: validated.applicantInfo.email,
      language: preferredLanguage,
      applicationNumber,
      permitType: validated.permitType
    });

    // Log for compliance audit
    await logAuditEvent({
      eventType: 'permit_application_submitted',
      applicationNumber,
      applicantEmail: validated.applicantInfo.email,
      permitType: validated.permitType,
      timestamp: new Date().toISOString(),
      ipAddress: 'REDACTED', // Get from request context
      userAgent: 'ChatGPT App'
    });

    return {
      content: [
        {
          type: 'text',
          text: `${t.submitted}!\n\n${t.applicationNumber}: ${applicationNumber}\n${t.estimatedReview}: ${reviewTimes[validated.permitType]}\n\n${t.nextSteps}:\n1. Check your email for confirmation\n2. ${t.checkStatus} https://permits.gov/status/${applicationNumber}\n3. You will receive updates via email and SMS`
        }
      ]
    };
  }
};

async function submitToPermitSystem(application: any) {
  // Integration with government permit management system
  // In production, this would call internal APIs
  console.log('Submitting permit application:', application);
}

async function sendConfirmationEmail(params: any) {
  // Email service integration
  console.log('Sending confirmation email:', params);
}

async function logAuditEvent(event: any) {
  // FedRAMP compliance audit logging
  console.log('Audit event logged:', event);
}

This tool handles the complete permit application workflow with built-in validation, multilingual support, and compliance logging.

Document Validation System

Validate uploaded documents meet government requirements:

// tools/document-validator.ts
import { z } from 'zod';
import type { MCPTool } from '@modelcontextprotocol/sdk';

const documentValidationSchema = z.object({
  documentUrl: z.string().url(),
  documentType: z.enum([
    'proof_of_ownership',
    'site_plan',
    'contractor_license',
    'insurance_certificate',
    'environmental_impact',
    'engineering_report'
  ]),
  permitType: z.string()
});

const validationRules = {
  proof_of_ownership: {
    maxSizeMB: 10,
    allowedFormats: ['pdf', 'jpg', 'png'],
    requiredText: ['owner', 'property', 'deed'],
    expirationRequired: false
  },
  contractor_license: {
    maxSizeMB: 5,
    allowedFormats: ['pdf'],
    requiredText: ['license', 'contractor', 'state'],
    expirationRequired: true,
    minimumValidMonths: 6
  },
  insurance_certificate: {
    maxSizeMB: 5,
    allowedFormats: ['pdf'],
    requiredText: ['insurance', 'liability', 'coverage'],
    expirationRequired: true,
    minimumValidMonths: 12,
    minimumCoverage: 1000000 // $1M liability
  },
  site_plan: {
    maxSizeMB: 20,
    allowedFormats: ['pdf', 'dwg', 'jpg', 'png'],
    requiredElements: ['scale', 'dimensions', 'property_lines'],
    expirationRequired: false
  }
};

export const documentValidatorTool: MCPTool = {
  name: 'validate_permit_document',
  description: 'Validate uploaded documents meet government requirements for permit applications',

  inputSchema: {
    type: 'object',
    properties: {
      documentUrl: {
        type: 'string',
        format: 'uri',
        description: 'Secure URL to uploaded document'
      },
      documentType: {
        type: 'string',
        enum: documentValidationSchema.shape.documentType.options
      },
      permitType: {
        type: 'string',
        description: 'Type of permit this document supports'
      }
    },
    required: ['documentUrl', 'documentType', 'permitType']
  },

  async execute(args: unknown) {
    const validated = documentValidationSchema.parse(args);
    const rules = validationRules[validated.documentType];
    const validationResults = {
      valid: true,
      errors: [] as string[],
      warnings: [] as string[],
      metadata: {} as any
    };

    try {
      // Download document metadata (not full content for privacy)
      const metadata = await fetchDocumentMetadata(validated.documentUrl);
      validationResults.metadata = metadata;

      // File size validation
      const fileSizeMB = metadata.sizeBytes / (1024 * 1024);
      if (fileSizeMB > rules.maxSizeMB) {
        validationResults.valid = false;
        validationResults.errors.push(
          `File size ${fileSizeMB.toFixed(2)}MB exceeds maximum ${rules.maxSizeMB}MB`
        );
      }

      // Format validation
      const fileExtension = metadata.filename.split('.').pop()?.toLowerCase();
      if (!rules.allowedFormats.includes(fileExtension || '')) {
        validationResults.valid = false;
        validationResults.errors.push(
          `File format .${fileExtension} not allowed. Accepted formats: ${rules.allowedFormats.join(', ')}`
        );
      }

      // OCR text extraction for content validation (if PDF/image)
      if (['pdf', 'jpg', 'png'].includes(fileExtension || '')) {
        const extractedText = await performOCR(validated.documentUrl);

        // Check for required text
        if (rules.requiredText) {
          const missingTerms = rules.requiredText.filter(term =>
            !extractedText.toLowerCase().includes(term.toLowerCase())
          );

          if (missingTerms.length > 0) {
            validationResults.valid = false;
            validationResults.errors.push(
              `Document missing required information: ${missingTerms.join(', ')}`
            );
          }
        }

        // Expiration date validation
        if (rules.expirationRequired) {
          const expirationDate = extractExpirationDate(extractedText);

          if (!expirationDate) {
            validationResults.valid = false;
            validationResults.errors.push('Expiration date not found in document');
          } else {
            const monthsUntilExpiration =
              (expirationDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24 * 30);

            if (monthsUntilExpiration < 0) {
              validationResults.valid = false;
              validationResults.errors.push('Document has expired');
            } else if (monthsUntilExpiration < rules.minimumValidMonths!) {
              validationResults.warnings.push(
                `Document expires in ${monthsUntilExpiration.toFixed(0)} months (minimum ${rules.minimumValidMonths} months recommended)`
              );
            }
          }
        }

        // Insurance coverage validation
        if (validated.documentType === 'insurance_certificate') {
          const coverage = extractCoverageAmount(extractedText);
          if (coverage < rules.minimumCoverage!) {
            validationResults.valid = false;
            validationResults.errors.push(
              `Liability coverage $${coverage.toLocaleString()} is below required minimum $${rules.minimumCoverage!.toLocaleString()}`
            );
          }
        }
      }

      // Accessibility check (ensure document is accessible)
      if (fileExtension === 'pdf') {
        const accessibilityCheck = await checkPDFAccessibility(validated.documentUrl);
        if (!accessibilityCheck.tagged) {
          validationResults.warnings.push(
            'PDF is not tagged for accessibility. Consider providing an accessible version.'
          );
        }
      }

      // Log validation attempt for audit
      await logAuditEvent({
        eventType: 'document_validation',
        documentType: validated.documentType,
        permitType: validated.permitType,
        valid: validationResults.valid,
        errors: validationResults.errors.length,
        timestamp: new Date().toISOString()
      });

      return {
        content: [
          {
            type: 'text',
            text: formatValidationReport(validationResults)
          }
        ]
      };

    } catch (error) {
      return {
        content: [
          {
            type: 'text',
            text: `Document validation failed: ${error.message}`
          }
        ],
        isError: true
      };
    }
  }
};

function formatValidationReport(results: any): string {
  let report = results.valid
    ? '✅ Document validation passed\n\n'
    : '❌ Document validation failed\n\n';

  if (results.errors.length > 0) {
    report += '**Errors:**\n';
    results.errors.forEach((err: string, i: number) => {
      report += `${i + 1}. ${err}\n`;
    });
    report += '\n';
  }

  if (results.warnings.length > 0) {
    report += '**Warnings:**\n';
    results.warnings.forEach((warn: string, i: number) => {
      report += `${i + 1}. ${warn}\n`;
    });
    report += '\n';
  }

  if (results.metadata) {
    report += `**Document details:**\n`;
    report += `- Filename: ${results.metadata.filename}\n`;
    report += `- Size: ${(results.metadata.sizeBytes / 1024).toFixed(0)} KB\n`;
    report += `- Uploaded: ${new Date(results.metadata.uploadedAt).toLocaleDateString()}\n`;
  }

  return report;
}

async function fetchDocumentMetadata(url: string) {
  // Fetch document metadata without downloading full content
  return {
    filename: 'example.pdf',
    sizeBytes: 2048000,
    uploadedAt: new Date().toISOString(),
    contentType: 'application/pdf'
  };
}

async function performOCR(url: string): Promise<string> {
  // OCR service integration (Google Cloud Vision, AWS Textract, etc.)
  return 'SAMPLE EXTRACTED TEXT FOR VALIDATION';
}

function extractExpirationDate(text: string): Date | null {
  // Parse expiration dates from various formats
  const datePatterns = [
    /expir(?:es|ation)[\s:]+(\d{1,2}\/\d{1,2}\/\d{2,4})/i,
    /valid\s+(?:until|through)[\s:]+(\d{1,2}\/\d{1,2}\/\d{2,4})/i
  ];

  for (const pattern of datePatterns) {
    const match = text.match(pattern);
    if (match) {
      return new Date(match[1]);
    }
  }

  return null;
}

function extractCoverageAmount(text: string): number {
  const coveragePattern = /liability[^\$]*\$?([\d,]+)/i;
  const match = text.match(coveragePattern);
  return match ? parseInt(match[1].replace(/,/g, '')) : 0;
}

async function checkPDFAccessibility(url: string) {
  // PDF accessibility validation
  return { tagged: true, hasAltText: true, hasBookmarks: true };
}

async function logAuditEvent(event: any) {
  console.log('Audit event:', event);
}

This validator ensures all documents meet government standards before submission.

Permit Application Status Tracking

Citizens need real-time visibility into their application status:

// tools/application-status.ts
import { z } from 'zod';
import type { MCPTool } from '@modelcontextprotocol/sdk';

const statusCheckSchema = z.object({
  applicationNumber: z.string().regex(/^[A-Z_]+-\d+-[A-Z0-9]{6}$/),
  notificationPreference: z.enum(['sms', 'email', 'both']).optional()
});

const statusTranslations = {
  en: {
    pending_review: 'Pending Review',
    under_review: 'Under Review',
    additional_info_required: 'Additional Information Required',
    approved: 'Approved',
    rejected: 'Rejected',
    expired: 'Expired'
  },
  es: {
    pending_review: 'Revisión Pendiente',
    under_review: 'En Revisión',
    additional_info_required: 'Información Adicional Requerida',
    approved: 'Aprobado',
    rejected: 'Rechazado',
    expired: 'Expirado'
  }
};

export const applicationStatusTool: MCPTool = {
  name: 'check_application_status',
  description: 'Check permit application status with timeline and next steps',

  inputSchema: {
    type: 'object',
    properties: {
      applicationNumber: {
        type: 'string',
        pattern: '^[A-Z_]+-\\d+-[A-Z0-9]{6}$',
        description: 'Unique application number (e.g., BUILDING_PERMIT-1234567890-ABC123)'
      },
      notificationPreference: {
        type: 'string',
        enum: ['sms', 'email', 'both'],
        description: 'How to receive status updates'
      }
    },
    required: ['applicationNumber']
  },

  async execute(args: unknown) {
    const validated = statusCheckSchema.parse(args);

    // Fetch application from database
    const application = await fetchApplication(validated.applicationNumber);

    if (!application) {
      return {
        content: [{
          type: 'text',
          text: `Application ${validated.applicationNumber} not found. Please verify the application number and try again.`
        }],
        isError: true
      };
    }

    // Update notification preferences if provided
    if (validated.notificationPreference) {
      await updateNotificationPreference(
        validated.applicationNumber,
        validated.notificationPreference
      );
    }

    // Build status timeline
    const timeline = buildStatusTimeline(application);
    const nextSteps = getNextSteps(application);

    return {
      content: [
        {
          type: 'text',
          text: formatStatusReport(application, timeline, nextSteps)
        }
      ]
    };
  }
};

function buildStatusTimeline(application: any) {
  return [
    {
      status: 'submitted',
      date: application.submittedAt,
      description: 'Application submitted',
      completed: true
    },
    {
      status: 'documents_verified',
      date: application.documentsVerifiedAt,
      description: 'Documents verified',
      completed: !!application.documentsVerifiedAt
    },
    {
      status: 'under_review',
      date: application.reviewStartedAt,
      description: 'Review in progress',
      completed: !!application.reviewStartedAt
    },
    {
      status: 'inspection_scheduled',
      date: application.inspectionScheduledAt,
      description: 'Inspection scheduled',
      completed: !!application.inspectionScheduledAt
    },
    {
      status: 'approved',
      date: application.approvedAt,
      description: 'Permit approved',
      completed: application.status === 'approved'
    }
  ];
}

function getNextSteps(application: any): string[] {
  const steps = [];

  if (application.status === 'additional_info_required') {
    steps.push(`Upload missing documents: ${application.requiredDocuments.join(', ')}`);
    steps.push('Deadline: ' + new Date(application.responseDeadline).toLocaleDateString());
  } else if (application.status === 'under_review') {
    steps.push('No action required - review in progress');
    steps.push(`Assigned reviewer: ${application.assignedReviewer}`);
    steps.push(`Expected completion: ${new Date(application.expectedCompletionDate).toLocaleDateString()}`);
  } else if (application.status === 'approved') {
    steps.push('Download permit: https://permits.gov/download/' + application.applicationNumber);
    steps.push('Pay permit fee: $' + application.permitFee.toLocaleString());
    steps.push('Schedule inspection (if required)');
  }

  return steps;
}

function formatStatusReport(application: any, timeline: any[], nextSteps: string[]): string {
  let report = `# Application Status: ${application.applicationNumber}\n\n`;
  report += `**Permit Type:** ${application.permitType.replace(/_/g, ' ').toUpperCase()}\n`;
  report += `**Current Status:** ${application.status.replace(/_/g, ' ').toUpperCase()}\n`;
  report += `**Submitted:** ${new Date(application.submittedAt).toLocaleDateString()}\n\n`;

  report += `## Timeline\n\n`;
  timeline.forEach(step => {
    const icon = step.completed ? '✅' : '⏳';
    const date = step.date ? new Date(step.date).toLocaleDateString() : 'Pending';
    report += `${icon} **${step.description}** - ${date}\n`;
  });

  if (nextSteps.length > 0) {
    report += `\n## Next Steps\n\n`;
    nextSteps.forEach((step, i) => {
      report += `${i + 1}. ${step}\n`;
    });
  }

  report += `\n---\n`;
  report += `Questions? Call (555) 123-4567 or email permits@agency.gov\n`;
  report += `Reference number: ${application.applicationNumber}`;

  return report;
}

async function fetchApplication(applicationNumber: string) {
  // Database query
  return {
    applicationNumber,
    permitType: 'building_permit',
    status: 'under_review',
    submittedAt: '2026-12-20T10:30:00Z',
    documentsVerifiedAt: '2026-12-21T14:00:00Z',
    reviewStartedAt: '2026-12-22T09:00:00Z',
    inspectionScheduledAt: null,
    approvedAt: null,
    assignedReviewer: 'Jane Smith',
    expectedCompletionDate: '2026-01-15T17:00:00Z',
    permitFee: 1500
  };
}

async function updateNotificationPreference(applicationNumber: string, preference: string) {
  console.log(`Updating notification preference for ${applicationNumber}: ${preference}`);
}

Real-time status tracking improves citizen satisfaction and reduces support inquiries.

Government Information Retrieval Systems

ChatGPT apps excel at making government information accessible to citizens:

Knowledge Base Search Tool

// tools/knowledge-base-search.ts
import { z } from 'zod';
import type { MCPTool } from '@modelcontextprotocol/sdk';

const searchSchema = z.object({
  query: z.string().min(3),
  category: z.enum([
    'permits_licenses',
    'tax_information',
    'public_records',
    'zoning_codes',
    'health_services',
    'transportation',
    'emergency_services'
  ]).optional(),
  language: z.enum(['en', 'es', 'zh', 'vi', 'ko', 'tl']).default('en')
});

export const knowledgeBaseSearchTool: MCPTool = {
  name: 'search_government_knowledge_base',
  description: 'Search government knowledge base for citizen information across all departments',

  inputSchema: {
    type: 'object',
    properties: {
      query: {
        type: 'string',
        minLength: 3,
        description: 'Search query in natural language'
      },
      category: {
        type: 'string',
        enum: searchSchema.shape.category!.options,
        description: 'Filter results by category'
      },
      language: {
        type: 'string',
        enum: ['en', 'es', 'zh', 'vi', 'ko', 'tl'],
        default: 'en'
      }
    },
    required: ['query']
  },

  async execute(args: unknown) {
    const validated = searchSchema.parse(args);

    // Semantic search with vector embeddings
    const searchResults = await performSemanticSearch({
      query: validated.query,
      category: validated.category,
      language: validated.language,
      limit: 5
    });

    // Track search for analytics
    await trackSearch({
      query: validated.query,
      category: validated.category,
      language: validated.language,
      resultsCount: searchResults.length,
      timestamp: new Date().toISOString()
    });

    if (searchResults.length === 0) {
      return {
        content: [{
          type: 'text',
          text: `No results found for "${validated.query}". Try rephrasing your question or contact support at (555) 123-4567.`
        }]
      };
    }

    return {
      content: [{
        type: 'text',
        text: formatSearchResults(searchResults, validated.language)
      }]
    };
  }
};

async function performSemanticSearch(params: any) {
  // Vector database search (Pinecone, Weaviate, etc.)
  return [
    {
      title: 'How to Apply for a Building Permit',
      url: 'https://agency.gov/kb/building-permits',
      summary: 'Complete guide to the building permit application process, required documents, and fees.',
      category: 'permits_licenses',
      relevanceScore: 0.95,
      lastUpdated: '2026-11-15'
    },
    {
      title: 'Building Permit Fee Schedule',
      url: 'https://agency.gov/kb/permit-fees',
      summary: 'Current fee structure for residential and commercial building permits.',
      category: 'permits_licenses',
      relevanceScore: 0.87,
      lastUpdated: '2026-12-01'
    }
  ];
}

function formatSearchResults(results: any[], language: string): string {
  let output = `Found ${results.length} result${results.length > 1 ? 's' : ''}:\n\n`;

  results.forEach((result, i) => {
    output += `**${i + 1}. ${result.title}**\n`;
    output += `${result.summary}\n`;
    output += `📄 Read more: ${result.url}\n`;
    output += `📅 Last updated: ${new Date(result.lastUpdated).toLocaleDateString()}\n\n`;
  });

  return output;
}

async function trackSearch(params: any) {
  console.log('Search tracked:', params);
}

Semantic search makes government information discoverable through natural language queries.

FAQ Answering System

// tools/faq-answering.ts
import { z } from 'zod';
import type { MCPTool } from '@modelcontextprotocol/sdk';

const faqSchema = z.object({
  question: z.string().min(10),
  department: z.enum([
    'dmv',
    'tax',
    'health',
    'building',
    'parks',
    'police',
    'fire'
  ]).optional()
});

const faqDatabase = {
  'How do I renew my driver\'s license?': {
    answer: 'You can renew your driver\'s license online at dmv.gov/renew, in person at any DMV office, or by mail if eligible. Online renewal is available if:\n\n1. Your license expires within 60 days\n2. You have no violations or suspensions\n3. You are between 18 and 70 years old\n\nRequired documents: Current license, proof of address, $35 renewal fee.',
    relatedLinks: [
      { title: 'Online Renewal Portal', url: 'https://dmv.gov/renew' },
      { title: 'DMV Office Locations', url: 'https://dmv.gov/offices' },
      { title: 'Accepted Documents', url: 'https://dmv.gov/documents' }
    ],
    department: 'dmv',
    lastUpdated: '2026-11-01'
  },
  'What are the business license requirements?': {
    answer: 'All businesses operating within city limits require a business license. Requirements vary by business type:\n\n**General Requirements:**\n- Completed application form\n- Proof of business location (lease/deed)\n- Federal EIN or SSN\n- $150-500 application fee (based on business type)\n\n**Additional Requirements by Type:**\n- Food service: Health permit, manager certification\n- Retail: Sales tax permit\n- Professional services: Professional license verification\n- Home-based: Zoning approval\n\nProcessing time: 5-7 business days.',
    relatedLinks: [
      { title: 'Apply Online', url: 'https://business.gov/license' },
      { title: 'Fee Schedule', url: 'https://business.gov/fees' },
      { title: 'Zoning Information', url: 'https://planning.gov/zoning' }
    ],
    department: 'building',
    lastUpdated: '2026-12-15'
  }
};

export const faqAnsweringTool: MCPTool = {
  name: 'answer_government_faq',
  description: 'Get instant answers to frequently asked government questions',

  inputSchema: {
    type: 'object',
    properties: {
      question: {
        type: 'string',
        minLength: 10,
        description: 'Question to answer'
      },
      department: {
        type: 'string',
        enum: faqSchema.shape.department!.options,
        description: 'Filter by department'
      }
    },
    required: ['question']
  },

  async execute(args: unknown) {
    const validated = faqSchema.parse(args);

    // Find best matching FAQ using semantic similarity
    const match = await findBestFAQMatch(validated.question, validated.department);

    if (!match || match.similarity < 0.7) {
      return {
        content: [{
          type: 'text',
          text: `I couldn't find a specific answer to "${validated.question}". Please try:\n\n1. Rephrasing your question\n2. Searching our knowledge base\n3. Calling (555) 123-4567 for assistance\n4. Visiting our help center: https://agency.gov/help`
        }]
      };
    }

    return {
      content: [{
        type: 'text',
        text: formatFAQAnswer(match.faq)
      }]
    };
  }
};

async function findBestFAQMatch(question: string, department?: string) {
  // Semantic similarity matching (cosine similarity of embeddings)
  // In production, use vector database

  const faqs = Object.entries(faqDatabase)
    .filter(([_, faq]) => !department || faq.department === department)
    .map(([q, faq]) => ({
      question: q,
      faq,
      similarity: calculateSimilarity(question, q)
    }))
    .sort((a, b) => b.similarity - a.similarity);

  return faqs[0];
}

function calculateSimilarity(q1: string, q2: string): number {
  // Simplified similarity - use embeddings in production
  const words1 = q1.toLowerCase().split(/\s+/);
  const words2 = q2.toLowerCase().split(/\s+/);
  const intersection = words1.filter(w => words2.includes(w));
  return intersection.length / Math.max(words1.length, words2.length);
}

function formatFAQAnswer(faq: any): string {
  let output = `${faq.answer}\n\n`;

  if (faq.relatedLinks.length > 0) {
    output += `**Related Resources:**\n`;
    faq.relatedLinks.forEach((link: any) => {
      output += `- ${link.title}\n`;
    });
    output += '\n';
  }

  output += `*Last updated: ${new Date(faq.lastUpdated).toLocaleDateString()}*`;

  return output;
}

Instant FAQ answers reduce support burden and improve citizen self-service.

Policy Document Finder

// tools/policy-document-finder.ts
import { z } from 'zod';
import type { MCPTool } from '@modelcontextprotocol/sdk';

const policySearchSchema = z.object({
  topic: z.string().min(3),
  jurisdiction: z.enum(['city', 'county', 'state', 'federal']).optional(),
  documentType: z.enum(['ordinance', 'regulation', 'code', 'statute', 'resolution']).optional()
});

export const policyDocumentFinderTool: MCPTool = {
  name: 'find_policy_documents',
  description: 'Search municipal codes, ordinances, and regulations',

  inputSchema: {
    type: 'object',
    properties: {
      topic: {
        type: 'string',
        minLength: 3,
        description: 'Topic or keyword to search for'
      },
      jurisdiction: {
        type: 'string',
        enum: ['city', 'county', 'state', 'federal']
      },
      documentType: {
        type: 'string',
        enum: ['ordinance', 'regulation', 'code', 'statute', 'resolution']
      }
    },
    required: ['topic']
  },

  async execute(args: unknown) {
    const validated = policySearchSchema.parse(args);

    const documents = await searchPolicyDocuments({
      topic: validated.topic,
      jurisdiction: validated.jurisdiction,
      documentType: validated.documentType
    });

    return {
      content: [{
        type: 'text',
        text: formatPolicyDocuments(documents)
      }]
    };
  }
};

async function searchPolicyDocuments(params: any) {
  // Search municipal code database
  return [
    {
      title: 'Chapter 15.04 - Building Code',
      citation: 'Municipal Code § 15.04',
      summary: 'Establishes building construction standards, permit requirements, and inspection procedures.',
      url: 'https://codes.agency.gov/15.04',
      effectiveDate: '2024-01-01',
      lastAmended: '2026-06-15'
    }
  ];
}

function formatPolicyDocuments(docs: any[]): string {
  if (docs.length === 0) return 'No policy documents found for this topic.';

  let output = `Found ${docs.length} policy document${docs.length > 1 ? 's' : ''}:\n\n`;

  docs.forEach((doc, i) => {
    output += `**${i + 1}. ${doc.title}**\n`;
    output += `📜 ${doc.citation}\n`;
    output += `${doc.summary}\n`;
    output += `🔗 Full text: ${doc.url}\n`;
    output += `📅 Effective: ${new Date(doc.effectiveDate).toLocaleDateString()}\n\n`;
  });

  return output;
}

For more advanced search implementations, see our guide on Widget Accessibility Advanced Techniques.

Accessibility Implementation for Government Apps

Section 508 compliance is mandatory for all government digital services. Here's how to ensure your ChatGPT app meets WCAG AAA standards:

WCAG AAA Compliance Checker

// tools/accessibility-checker.ts
import { z } from 'zod';
import type { MCPTool } from '@modelcontextprotocol/sdk';

const accessibilityCheckSchema = z.object({
  widgetHtml: z.string(),
  level: z.enum(['A', 'AA', 'AAA']).default('AAA')
});

export const accessibilityCheckerTool: MCPTool = {
  name: 'check_wcag_compliance',
  description: 'Validate widget HTML against WCAG accessibility standards',

  inputSchema: {
    type: 'object',
    properties: {
      widgetHtml: {
        type: 'string',
        description: 'HTML content to validate'
      },
      level: {
        type: 'string',
        enum: ['A', 'AA', 'AAA'],
        default: 'AAA'
      }
    },
    required: ['widgetHtml']
  },

  async execute(args: unknown) {
    const validated = accessibilityCheckSchema.parse(args);

    const issues = {
      critical: [] as string[],
      warnings: [] as string[],
      recommendations: [] as string[]
    };

    // Color contrast validation
    const contrastIssues = checkColorContrast(validated.widgetHtml);
    issues.critical.push(...contrastIssues.critical);
    issues.warnings.push(...contrastIssues.warnings);

    // Semantic HTML validation
    const semanticIssues = checkSemanticHTML(validated.widgetHtml);
    issues.critical.push(...semanticIssues);

    // ARIA validation
    const ariaIssues = checkARIA(validated.widgetHtml);
    issues.critical.push(...ariaIssues.critical);
    issues.recommendations.push(...ariaIssues.recommendations);

    // Keyboard navigation
    const keyboardIssues = checkKeyboardNavigation(validated.widgetHtml);
    issues.critical.push(...keyboardIssues);

    // Alternative text
    const altTextIssues = checkAlternativeText(validated.widgetHtml);
    issues.critical.push(...altTextIssues);

    const passed = issues.critical.length === 0;

    return {
      content: [{
        type: 'text',
        text: formatAccessibilityReport(issues, validated.level, passed)
      }]
    };
  }
};

function checkColorContrast(html: string) {
  const issues = { critical: [] as string[], warnings: [] as string[] };

  // Parse color values and calculate contrast ratios
  // WCAG AAA requires 7:1 for normal text, 4.5:1 for large text

  return issues;
}

function checkSemanticHTML(html: string): string[] {
  const issues = [];

  if (!html.includes('<main') && !html.includes('role="main"')) {
    issues.push('Missing main landmark region');
  }

  if (html.includes('<div') && html.match(/<div[^>]*>\s*<div/g)) {
    issues.push('Excessive div nesting - use semantic HTML elements');
  }

  return issues;
}

function checkARIA(html: string) {
  const issues = { critical: [] as string[], recommendations: [] as string[] };

  // Validate ARIA attributes
  const invalidRoles = html.match(/role="[^"]*"/g)?.filter(role =>
    !['button', 'navigation', 'main', 'region', 'complementary'].includes(
      role.replace(/role="|"/g, '')
    )
  );

  if (invalidRoles) {
    issues.critical.push(`Invalid ARIA roles: ${invalidRoles.join(', ')}`);
  }

  return issues;
}

function checkKeyboardNavigation(html: string): string[] {
  const issues = [];

  if (html.includes('onclick') && !html.includes('onkeypress')) {
    issues.push('Click handlers missing keyboard equivalents');
  }

  return issues;
}

function checkAlternativeText(html: string): string[] {
  const issues = [];

  const imagesWithoutAlt = html.match(/<img(?![^>]*alt=)[^>]*>/g);
  if (imagesWithoutAlt) {
    issues.push(`${imagesWithoutAlt.length} images missing alt text`);
  }

  return issues;
}

function formatAccessibilityReport(issues: any, level: string, passed: boolean): string {
  let report = `# WCAG ${level} Accessibility Report\n\n`;
  report += passed
    ? '✅ **PASSED** - No critical accessibility issues found\n\n'
    : '❌ **FAILED** - Critical accessibility issues detected\n\n';

  if (issues.critical.length > 0) {
    report += `## Critical Issues (${issues.critical.length})\n\n`;
    issues.critical.forEach((issue: string, i: number) => {
      report += `${i + 1}. ${issue}\n`;
    });
    report += '\n';
  }

  if (issues.warnings.length > 0) {
    report += `## Warnings (${issues.warnings.length})\n\n`;
    issues.warnings.forEach((issue: string, i: number) => {
      report += `${i + 1}. ${issue}\n`;
    });
    report += '\n';
  }

  if (issues.recommendations.length > 0) {
    report += `## Recommendations (${issues.recommendations.length})\n\n`;
    issues.recommendations.forEach((issue: string, i: number) => {
      report += `${i + 1}. ${issue}\n`;
    });
  }

  return report;
}

Screen Reader Optimizer

// tools/screen-reader-optimizer.ts
import { z } from 'zod';
import type { MCPTool } from '@modelcontextprotocol/sdk';

export const screenReaderOptimizerTool: MCPTool = {
  name: 'optimize_for_screen_readers',
  description: 'Enhance HTML for optimal screen reader experience',

  inputSchema: {
    type: 'object',
    properties: {
      html: { type: 'string' }
    },
    required: ['html']
  },

  async execute(args: any) {
    let optimized = args.html;

    // Add ARIA landmarks
    optimized = addLandmarkRegions(optimized);

    // Enhance form labels
    optimized = enhanceFormAccessibility(optimized);

    // Add skip navigation links
    optimized = addSkipLinks(optimized);

    // Enhance table accessibility
    optimized = enhanceTableAccessibility(optimized);

    return {
      content: [{
        type: 'text',
        text: `Optimized HTML for screen readers:\n\n\`\`\`html\n${optimized}\n\`\`\``
      }]
    };
  }
};

function addLandmarkRegions(html: string): string {
  // Add ARIA landmarks for screen reader navigation
  if (!html.includes('<main')) {
    html = html.replace(/<div class="content">/, '<main role="main">');
    html = html.replace(/<\/div>\s*<\/body>/, '</main></body>');
  }
  return html;
}

function enhanceFormAccessibility(html: string): string {
  // Ensure all inputs have associated labels
  return html.replace(
    /<input([^>]*)>/g,
    (match, attrs) => {
      if (!attrs.includes('aria-label') && !attrs.includes('id')) {
        const id = `input-${Math.random().toString(36).substr(2, 9)}`;
        return `<input${attrs} id="${id}" aria-describedby="${id}-desc">`;
      }
      return match;
    }
  );
}

function addSkipLinks(html: string): string {
  const skipLink = '<a href="#main-content" class="skip-link">Skip to main content</a>';
  return html.replace(/<body[^>]*>/, `$&\n${skipLink}`);
}

function enhanceTableAccessibility(html: string): string {
  // Add scope and headers attributes to table cells
  return html.replace(/<table/g, '<table role="table"')
    .replace(/<th/g, '<th scope="col"');
}

Multilingual Support System

// tools/multilingual-support.ts
import { z } from 'zod';
import type { MCPTool } from '@modelcontextprotocol/sdk';

const translateSchema = z.object({
  text: z.string(),
  targetLanguage: z.enum(['es', 'zh', 'vi', 'ko', 'tl']),
  context: z.enum(['legal', 'technical', 'general']).default('general')
});

export const multilingualSupportTool: MCPTool = {
  name: 'translate_government_content',
  description: 'Translate government content with context-aware terminology',

  inputSchema: {
    type: 'object',
    properties: {
      text: { type: 'string' },
      targetLanguage: {
        type: 'string',
        enum: ['es', 'zh', 'vi', 'ko', 'tl']
      },
      context: {
        type: 'string',
        enum: ['legal', 'technical', 'general'],
        default: 'general'
      }
    },
    required: ['text', 'targetLanguage']
  },

  async execute(args: unknown) {
    const validated = translateSchema.parse(args);

    // Use government-specific translation glossary
    const translated = await translateWithGlossary({
      text: validated.text,
      targetLanguage: validated.targetLanguage,
      context: validated.context,
      glossary: governmentTerminology
    });

    return {
      content: [{
        type: 'text',
        text: translated
      }]
    };
  }
};

const governmentTerminology = {
  'permit': { es: 'permiso', zh: '许可证', vi: 'giấy phép', ko: '허가', tl: 'permiso' },
  'application': { es: 'solicitud', zh: '申请', vi: 'đơn xin', ko: '신청', tl: 'aplikasyon' },
  'fee': { es: 'tarifa', zh: '费用', vi: 'phí', ko: '수수료', tl: 'bayad' }
  // ... comprehensive glossary
};

async function translateWithGlossary(params: any): Promise<string> {
  // Translation service integration with terminology enforcement
  return params.text; // Mock
}

For comprehensive accessibility implementation guidance, review our Security Audit Compliance Checklist.

Security & FedRAMP Compliance

Government ChatGPT apps must implement comprehensive security and audit logging:

FedRAMP Audit Logger

// tools/audit-logger.ts
import { z } from 'zod';
import type { MCPTool } from '@modelcontextprotocol/sdk';

const auditEventSchema = z.object({
  eventType: z.enum([
    'user_authentication',
    'data_access',
    'data_modification',
    'permission_change',
    'system_configuration',
    'security_incident'
  ]),
  userId: z.string().optional(),
  resourceId: z.string().optional(),
  details: z.record(z.any())
});

export const auditLoggerTool: MCPTool = {
  name: 'log_audit_event',
  description: 'Log security events for FedRAMP compliance auditing',

  inputSchema: {
    type: 'object',
    properties: {
      eventType: {
        type: 'string',
        enum: auditEventSchema.shape.eventType.options
      },
      userId: { type: 'string' },
      resourceId: { type: 'string' },
      details: { type: 'object' }
    },
    required: ['eventType', 'details']
  },

  async execute(args: unknown) {
    const validated = auditEventSchema.parse(args);

    const auditRecord = {
      eventId: generateEventId(),
      timestamp: new Date().toISOString(),
      eventType: validated.eventType,
      userId: validated.userId || 'anonymous',
      resourceId: validated.resourceId,
      ipAddress: 'REDACTED', // Get from request context
      userAgent: 'ChatGPT App',
      details: validated.details,
      severity: calculateSeverity(validated.eventType)
    };

    // Write to immutable audit log
    await writeAuditLog(auditRecord);

    // Send high-severity events to SIEM
    if (auditRecord.severity === 'high' || auditRecord.severity === 'critical') {
      await sendToSIEM(auditRecord);
    }

    return {
      content: [{
        type: 'text',
        text: `Audit event logged: ${auditRecord.eventId}`
      }]
    };
  }
};

function generateEventId(): string {
  return `AUD-${Date.now()}-${Math.random().toString(36).substr(2, 9).toUpperCase()}`;
}

function calculateSeverity(eventType: string): string {
  const severityMap = {
    security_incident: 'critical',
    permission_change: 'high',
    data_modification: 'medium',
    data_access: 'low',
    user_authentication: 'low',
    system_configuration: 'high'
  };
  return severityMap[eventType] || 'low';
}

async function writeAuditLog(record: any) {
  // Write to immutable storage (WORM - Write Once Read Many)
  console.log('Audit log written:', record);
}

async function sendToSIEM(record: any) {
  // Send to Security Information and Event Management system
  console.log('SIEM alert sent:', record);
}

PII Encryption Tool

// tools/pii-encryption.ts
import { z } from 'zod';
import type { MCPTool } from '@modelcontextprotocol/sdk';
import * as crypto from 'crypto';

const encryptionSchema = z.object({
  data: z.record(z.any()),
  piiFields: z.array(z.string())
});

export const piiEncryptionTool: MCPTool = {
  name: 'encrypt_pii_data',
  description: 'Encrypt personally identifiable information for FedRAMP compliance',

  inputSchema: {
    type: 'object',
    properties: {
      data: { type: 'object' },
      piiFields: {
        type: 'array',
        items: { type: 'string' }
      }
    },
    required: ['data', 'piiFields']
  },

  async execute(args: unknown) {
    const validated = encryptionSchema.parse(args);
    const encrypted = { ...validated.data };

    for (const field of validated.piiFields) {
      if (encrypted[field]) {
        encrypted[field] = await encryptField(
          encrypted[field],
          process.env.ENCRYPTION_KEY!
        );
      }
    }

    return {
      content: [{
        type: 'text',
        text: JSON.stringify(encrypted, null, 2)
      }]
    };
  }
};

async function encryptField(value: string, key: string): Promise<string> {
  const iv = crypto.randomBytes(16);
  const cipher = crypto.createCipheriv('aes-256-gcm', Buffer.from(key, 'hex'), iv);

  let encrypted = cipher.update(value, 'utf8', 'hex');
  encrypted += cipher.final('hex');

  const authTag = cipher.getAuthTag();

  return `${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted}`;
}

Production Deployment Checklist

Before deploying your government ChatGPT app:

Security & Compliance

  • FedRAMP security controls implemented
  • Comprehensive audit logging enabled
  • PII encryption at rest and in transit
  • Role-based access control (RBAC) configured
  • Penetration testing completed
  • Authority to Operate (ATO) obtained

Accessibility

  • WCAG 2.1 Level AAA compliance verified
  • Section 508 checklist completed
  • Screen reader testing (JAWS, NVDA, VoiceOver)
  • Keyboard navigation fully functional
  • Color contrast ratios validated (7:1 minimum)
  • Alternative text for all visual content

Multilingual Support

  • All content translated by certified translators
  • Government terminology glossary applied
  • Language detection and switching implemented
  • Right-to-left language support (Arabic, Hebrew) if needed

Performance & Reliability

  • 99.9% uptime SLA established
  • Load testing completed (1000+ concurrent users)
  • Disaster recovery plan documented
  • Backup systems tested
  • Monitoring and alerting configured

User Experience

  • User acceptance testing with diverse populations
  • Plain language review completed
  • Mobile responsiveness verified
  • Help documentation in all supported languages
  • Support contact information clearly displayed

Conclusion: Transforming Government Service Delivery

ChatGPT apps represent a paradigm shift in government service delivery—from one-size-fits-all web forms to intelligent, conversational interfaces that adapt to each citizen's needs. By implementing the production-ready code in this guide, you can deploy accessible, secure, and efficient government services that serve all citizens 24/7.

Key takeaways:

  1. Accessibility is mandatory: Section 508 and WCAG AAA compliance aren't optional for government apps—build accessibility in from day one
  2. Security enables trust: FedRAMP-grade security, audit logging, and PII encryption protect citizen data and maintain public trust
  3. Multilingual support expands access: Government services must be accessible to non-English speakers—build translation and localization from the start
  4. Conversational UI reduces barriers: ChatGPT's natural language interface makes government services accessible to citizens with varying technical literacy

Ready to modernize your government agency? Start building with MakeAIHQ and deploy your first citizen service portal to the ChatGPT App Store in 48 hours—no coding required.

For more government application strategies, explore our Complete Guide to ChatGPT Applications.


About MakeAIHQ: We're the only no-code platform specifically designed for the ChatGPT App Store, helping government agencies, businesses, and organizations reach 800 million ChatGPT users without writing code.

Related Resources:

External Resources: