Skip to main content
Glama
monostate

Crossmint HR Airdrop MCP

by monostate

start_airdrop

Distribute Solana tokens to employees based on roles and automate email notifications for HR teams using Crossmint’s airdrop solution.

Instructions

Perform the token airdrop to employee wallets

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • Primary handler for the start_airdrop tool. Validates prerequisites (connected wallet, created token, employee list with amounts), optionally prompts for Helius API key, then calls sendCompressedAirdrop utility to distribute tokens to employee wallets and updates state.
    private async handleStartAirdrop() { if (this.state.employees.length === 0) { throw new McpError( ErrorCode.InvalidRequest, 'No employees added. Please generate wallets first.' ); } if (!this.state.connectedWallet) { throw new McpError( ErrorCode.InvalidRequest, 'No wallet connected. Please connect a wallet first.' ); } if (!this.state.createdToken) { throw new McpError( ErrorCode.InvalidRequest, 'No token created. Please create a token first.' ); } // Check if token amounts are calculated const missingAmounts = this.state.employees.some((e) => e.tokenAmount === undefined); if (missingAmounts) { throw new McpError( ErrorCode.InvalidRequest, 'Token amounts not calculated for all employees. Please calculate amounts first.' ); } // Check if Helius API key is available in environment variables if (!process.env.HELIUS_API_KEY) { return { content: [ { type: 'text', text: `For optimal performance with compressed airdrops, I recommend using a Helius API key. You can get one from https://www.helius.dev/ Please provide your Helius API key to continue, or type "skip" to proceed without one.`, }, ], }; } try { // Create a Solana connection const connection = new Connection( 'https://api.mainnet-beta.solana.com', 'confirmed' ); // Create a keypair from the connected wallet (in a real implementation) // For this demo, we'll use a generated keypair const keypair = Keypair.generate(); // Prepare recipients for the airdrop const recipients = this.state.employees.map(employee => ({ address: employee.walletAddress, email: employee.email })); // Use the ZK light protocol to perform the airdrop // In a real implementation, this would call the actual sendCompressedAirdrop function // For this demo, we'll simulate the airdrop // Use each employee's calculated token amount const result = await sendCompressedAirdrop( connection, keypair, this.state.createdToken.mintAddress, 0, // Not used - individual amounts are in the employees array this.state.createdToken.decimals, this.state.employees.map(employee => ({ address: employee.walletAddress, email: employee.email, amount: employee.tokenAmount || 100 // Use calculated amount or default to 100 })), process.env.HELIUS_API_KEY // Use Helius API key for compressed transactions if available ); // Update airdrop status this.state.airdropStatus = { started: true, completed: true, successful: this.state.employees.length, failed: 0, }; } catch (error) { throw new McpError( ErrorCode.InternalError, `Airdrop failed: ${error instanceof Error ? error.message : String(error)}` ); } return { content: [ { type: 'text', text: ` Airdrop completed successfully: - Total employees: ${this.state.employees.length} - Successful transfers: ${this.state.airdropStatus.successful} - Failed transfers: ${this.state.airdropStatus.failed} Each employee has received their allocated tokens: ${this.state.employees .map( (employee) => `- ${employee.name || employee.email}: ${employee.tokenAmount} ${ this.state.createdToken?.symbol } tokens` ) .join('\n')} Next step: Send emails to employees with wallet access instructions. `.trim(), }, ], }; }
  • src/server.ts:267-274 (registration)
    Tool registration in ListToolsRequestSchema handler, defining name, description, and empty input schema (no parameters required).
    { name: 'start_airdrop', description: 'Perform the token airdrop to employee wallets', inputSchema: { type: 'object', properties: {}, }, },
  • Input schema for start_airdrop tool: empty object properties (no input parameters required).
    inputSchema: { type: 'object', properties: {}, },
  • Core helper function implementing the batched token airdrop logic using SPL Token transfer instructions, associated token accounts creation if needed, and Helius RPC support for compression. Called by the main handler.
    export async function sendCompressedAirdrop( connection: Connection, sender: Keypair, mintAddress: string, amount: number, decimals: number, recipients: { address: string, email: string, amount?: number }[], heliusApiKey?: string ): Promise<{ txIds: string[] }> { // Use Helius RPC for compressed transactions if API key is provided const heliusConnection = heliusApiKey ? new Connection(`https://mainnet.helius-rpc.com/?api-key=${heliusApiKey}`, 'confirmed') : connection; try { console.log(`Sending airdrop to ${recipients.length} recipients`); console.log(`Using ${heliusApiKey ? 'Helius' : 'default'} RPC for compressed transactions`); // Convert mintAddress string to PublicKey const mintPublicKey = new PublicKey(mintAddress); // Get the sender's token account const senderTokenAccount = await getAssociatedTokenAddress( mintPublicKey, sender.publicKey ); // Check if the sender's token account exists try { await getAccount(connection, senderTokenAccount); } catch (error) { throw new Error(`Sender's token account does not exist: ${error instanceof Error ? error.message : String(error)}`); } // Batch recipients into groups of 5 (to avoid transaction size limits) const batchSize = 5; const batches = []; for (let i = 0; i < recipients.length; i += batchSize) { batches.push(recipients.slice(i, i + batchSize)); } console.log(`Processing ${batches.length} batches of recipients`); // Process each batch const txIds = []; for (let i = 0; i < batches.length; i++) { const batch = batches[i]; console.log(`Processing batch ${i + 1}/${batches.length} with ${batch.length} recipients`); // Create a transaction for this batch const transaction = new Transaction(); // Add transfer instructions for each recipient in the batch for (const recipient of batch) { // Convert recipient address string to PublicKey const recipientPublicKey = new PublicKey(recipient.address); // Get the recipient's token account const recipientTokenAccount = await getAssociatedTokenAddress( mintPublicKey, recipientPublicKey ); // Check if the recipient's token account exists let recipientAccountExists = true; try { await getAccount(connection, recipientTokenAccount); } catch (error) { recipientAccountExists = false; } // If the recipient's token account doesn't exist, create it if (!recipientAccountExists) { transaction.add( createAssociatedTokenAccountInstruction( sender.publicKey, recipientTokenAccount, recipientPublicKey, mintPublicKey ) ); } // Calculate the amount to send (use the recipient's custom amount if provided) const sendAmount = (recipient.amount || amount) * Math.pow(10, decimals); // Add the transfer instruction transaction.add( createTransferInstruction( senderTokenAccount, recipientTokenAccount, sender.publicKey, sendAmount ) ); } // Send the transaction try { const signature = await sendAndConfirmTransaction( connection, transaction, [sender] ); console.log(`Batch ${i + 1} sent with transaction ID: ${signature}`); txIds.push(signature); } catch (error) { console.error(`Error sending batch ${i + 1}:`, error); throw error; } } return { txIds }; } catch (error) { console.error('Error sending compressed airdrop:', error); throw new Error(`Compressed airdrop failed: ${error instanceof Error ? error.message : String(error)}`); } }

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