Skip to main content
Glama
monostate

Crossmint HR Airdrop MCP

by monostate

upload_csv

Process employee data from a CSV file to enable Solana token airdrops, role-based allocations, and automated email notifications for corporate HR teams.

Instructions

Process employee data from a CSV file

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
filePathYesPath to the CSV file

Implementation Reference

  • src/server.ts:221-234 (registration)
    Registers the 'upload_csv' tool in the ListToolsRequestSchema handler, including name, description, and inputSchema for filePath.
    {
      name: 'upload_csv',
      description: 'Process employee data from a CSV file',
      inputSchema: {
        type: 'object',
        properties: {
          filePath: {
            type: 'string',
            description: 'Path to the CSV file',
          },
        },
        required: ['filePath'],
      },
    },
  • src/server.ts:326-327 (registration)
    Dispatches 'upload_csv' tool calls to the handleUploadCsv method in the CallToolRequestSchema handler.
    case 'upload_csv':
      return await this.handleUploadCsv(args);
  • The primary handler for the 'upload_csv' tool. Validates input, reads and parses CSV using csv-parse/sync, matches records to existing employee wallets by email, updates names and roles, validates roles, updates server state, and returns role distribution summary.
      private async handleUploadCsv(args: any) {
        // Validate input
        const schema = z.object({
          filePath: z.string(),
        });
        
        const { filePath } = schema.parse(args);
        
        try {
          // Read and parse CSV file
          const fileContent = fs.readFileSync(filePath, 'utf8');
          const records = csvParse(fileContent, {
            columns: true,
            skip_empty_lines: true,
          });
          
          // Validate records
          const validRoles = ['operational', 'developer', 'manager', 'VP', 'VIP'];
          
          if (!records.length) {
            throw new McpError(ErrorCode.InvalidParams, 'CSV file is empty.');
          }
          
          // Check required columns
          const firstRecord = records[0];
          if (!('email' in firstRecord)) {
            throw new McpError(ErrorCode.InvalidParams, 'CSV file must have an "email" column.');
          }
          
          // Map existing emails to records
          const employeesByEmail = new Map(
            this.state.employees.map((e) => [e.email, e])
          );
          
          // Update or create employee records
          const updatedEmployees = records.map((record: any) => {
            if (!record.email) {
              throw new McpError(ErrorCode.InvalidParams, 'Every row must have an email.');
            }
            
            const existingEmployee = employeesByEmail.get(record.email);
            if (!existingEmployee) {
              throw new McpError(
                ErrorCode.InvalidParams,
                `Email ${record.email} does not match any generated wallet. Please generate wallets first.`
              );
            }
            
            if (record.role && !validRoles.includes(record.role.toLowerCase())) {
              throw new McpError(
                ErrorCode.InvalidParams,
                `Invalid role "${record.role}" for ${record.email}. Valid roles are: ${validRoles.join(', ')}.`
              );
            }
            
            return {
              ...existingEmployee,
              name: record.name || undefined,
              role: record.role?.toLowerCase() || undefined,
            };
          });
          
          // Update state
          this.state.employees = updatedEmployees;
          
          return {
            content: [
              {
                type: 'text',
                text: `
    CSV data processed successfully. Updated ${updatedEmployees.length} employee records.
    
    Role distribution:
    - Operational: ${updatedEmployees.filter((e: any) => e.role === 'operational').length}
    - Developer: ${updatedEmployees.filter((e: any) => e.role === 'developer').length}
    - Manager: ${updatedEmployees.filter((e: any) => e.role === 'manager').length}
    - VP: ${updatedEmployees.filter((e: any) => e.role === 'vp').length}
    - VIP: ${updatedEmployees.filter((e: any) => e.role === 'vip').length}
    - No role: ${updatedEmployees.filter((e: any) => !e.role).length}
    
    Next step: Calculate token amounts for each employee.
                `.trim(),
              },
            ],
          };
        } catch (error) {
          if (error instanceof McpError) throw error;
          throw new McpError(
            ErrorCode.InternalError,
            `Failed to process CSV: ${error instanceof Error ? error.message : String(error)}`
          );
        }
      }
  • Zod schema for input validation within the handler (filePath: string).
    const schema = z.object({
      filePath: z.string(),
    });
Install Server

Other Tools

Related 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/monostate/Employees-Airdrop-Rewards-MCP'

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