Snappy MCP Server Integration Guide

Overview

This document provides a comprehensive guide for integrating external AI systems (su ch as Salesforce AgentForce or Telegram bots) with our MCP (Model Context Protocol) server for automated gift campaign management.

Prerequisites

Before integrating with our MCP server, ensure the following are already set up in your Snappy dashboard:

🏢 Account Setup Required

  • User Account: Valid user credentials (username/password + optional TOTP)
  • Company: At least one company associated with your account
  • Account: At least one account under your company
  • Collections: One or more product collections configured for your account
  • Recipients: Contact lists or individual recipients (can be managed via API)

AgentForce Integration

🤖 Salesforce AgentForce Setup

1. MCP Server Configuration

// AgentForce MCP Configuration
const mcpConfig = {
  serverUrl: "<https://your-mcp-server.com/mcp>",
  transport: "http", // or "sse" for real-time updates
  authentication: {
    type: "user_credentials",
    endpoint: "/auth/sign-in"
  }
};

2. AgentForce Action Configuration

// Salesforce Apex class for MCP integration
public class SnappyGiftMCPIntegration {

    @InvocableMethod(label='Send Snappy Gift Campaign')
    public static List<GiftCampaignResult> sendGiftCampaign(List<GiftCampaignRequest> requests) {
        List<GiftCampaignResult> results = new List<GiftCampaignResult>();

        for (GiftCampaignRequest request : requests) {
            try {
                // 1. Authenticate with MCP server
                String sessionToken = authenticateUser(request.username, request.password);

                // 2. Execute gift campaign workflow
                GiftCampaignResult result = executeGiftWorkflow(sessionToken, request);
                results.add(result);

            } catch (Exception e) {
                results.add(new GiftCampaignResult(false, e.getMessage()));
            }
        }
        return results;
    }

    private static String authenticateUser(String username, String password) {
        // Call MCP server sign-in tool
        HttpRequest req = new HttpRequest();
        req.setEndpoint('<https://your-mcp-server.com/mcp>');
        req.setMethod('POST');
        req.setHeader('Content-Type', 'application/json');

        Map<String, Object> mcpRequest = new Map<String, Object>{
            'method' => 'tools/call',
            'params' => new Map<String, Object>{
                'name' => 'sign-in',
                'arguments' => new Map<String, Object>{
                    'username' => username,
                    'password' => password
                }
            }
        };

        req.setBody(JSON.serialize(mcpRequest));

        HttpResponse res = new Http().send(req);
        // Parse response and extract session token
        return parseAuthResponse(res.getBody());
    }
}

3. AgentForce Topic Configuration

# AgentForce Topic: Gift Campaign Management
topic_name: "Gift Campaign Management"
description: "Automate gift campaigns through Snappy integration"

# Available Actions
actions:
  - name: "Create Gift Campaign"
    description: "Create and send a personalized gift campaign"
    parameters:
      - recipient_email: "Email of the gift recipient"
      - occasion: "Occasion for the gift (birthday, work anniversary, etc.)"
      - budget_range: "Budget range for gifts ($25-$50, $50-$100, etc.)"
      - collection_preference: "Preferred collection type (optional)"
      - custom_message: "Personal message for the recipient"

# Integration with MCP Tools
mcp_tools:
  - "sign-in"
  - "get-account-collections"
  - "get-collection-products"
  - "create-campaign"
  - "send-gifts"
  - "create-email"
  - "create-greeting"
  - "choose-reveal"

Authentication & Authorization

🔐 Authentication Flow

sequenceDiagram
    participant AF as AgentForce
    participant MCP as MCP Server
    participant Auth as AWS Cognito
    participant Snappy as Snappy APIs

    AF->>MCP: POST /mcp (sign-in tool)
    MCP->>Auth: Authenticate user
    Auth-->>MCP: JWT tokens
    MCP-->>AF: Success + session established
    AF->>MCP: Subsequent tool calls
    MCP->>Snappy: API calls with token
    Snappy-->>MCP: Data response
    MCP-->>AF: Tool response

Step 1: User Authentication

{
  "method": "tools/call",
  "params": {
    "name": "sign-in",
    "arguments": {
      "username": "[email protected]",
      "password": "secure_password",
      "totp": "123456"
    }
  }
}

Response:

{
  "content": [
    {
      "type": "text",
      "text": "Successfully signed in. Available accounts: [Account data]"
    }
  ]
}

Step 2: Session Management

For HTTP transport: Each request must include authentication
For SSE transport: Session is maintained automatically after sign-in

Available Tools & Endpoints

📋 Core Tools Reference

Tool NamePurposeRequired AuthKey Parameters
sign-inUser authenticationNousername, password, totp?
check-sign-inVerify auth statusNoNone
get-account-collectionsList available collectionsYesaccountId
get-collection-productsGet products in collectionYescollectionId, maxBudget?, country?
create-campaignCreate gift campaignYescompanyId, accountId, collectionId, name, budgets, recipientsIds
send-giftsSend campaign giftsYescampaignId, recipients, budget, productCollectionId
create-emailGenerate email contentYesoccasion, tone?, primaryColor?
create-greetingGenerate greeting cardYesoccasion, tone?, primaryColor?
choose-revealSelect gift reveal experienceYesoccasion, brandColors?

🔍 Tool Details

get-account-collections

{
  "method": "tools/call",
  "params": {
    "name": "get-account-collections",
    "arguments": {
      "accountId": "account_12345"
    }
  }
}

Response:

{
  "content": [{
    "type": "text",
    "text": "[{\\"_id\\":\\"coll_123\\",\\"name\\":\\"Premium Business Collection\\",\\"rank\\":1},{\\"_id\\":\\"coll_456\\",\\"name\\":\\"Holiday Special Collection\\",\\"rank\\":2}]"
  }]
}

create-campaign

{
  "method": "tools/call",
  "params": {
    "name": "create-campaign",
    "arguments": {
      "companyId": "comp_789",
      "accountId": "acc_123",
      "collectionId": "coll_456",
      "name": "Q4 Employee Appreciation Campaign",
      "maxBudget": 75,
      "minBudget": 25,
      "recipientsIds": ["recipient_1", "recipient_2"],
      "email": {
        "mailSubject": "A special gift for you!",
        "mailGreeting": "Dear {receiver_first_name}",
        "mailBody": "We appreciate your hard work this quarter.",
        "mailSignature": "Best regards, The Team"
      }
    }
  }
}

Complete Workflow Examples

🎯 Example 1: AgentForce Birthday Campaign

// Salesforce Flow: Birthday Gift Campaign
public class BirthdayGiftFlow {

    @InvocableMethod
    public static void sendBirthdayGift(List<EmployeeRecord> employees) {

        for (EmployeeRecord emp : employees) {
            // Step 1: Authenticate
            MCPSession session = MCPClient.authenticate(
                System.Label.SnappyUsername,
                System.Label.SnappyPassword
            );

            // Step 2: Get collections
            List<Collection> collections = session.getAccountCollections(emp.AccountId);
            Collection birthdayCollection = findBirthdayCollection(collections);

            // Step 3: Create personalized content
            EmailContent email = session.createEmail(new Map<String, Object>{
                'occasion' => 'birthday',
                'tone' => 'warm and celebratory'
            });

            GreetingContent greeting = session.createGreeting(new Map<String, Object>{
                'occasion' => 'birthday',
                'tone' => 'personal'
            });

            // Step 4: Create campaign
            Campaign campaign = session.createCampaign(new Map<String, Object>{
                'companyId' => emp.CompanyId,
                'accountId' => emp.AccountId,
                'collectionId' => birthdayCollection.Id,
                'name' => emp.FirstName + ' Birthday Gift ' + Date.today().year(),
                'maxBudget' => 50,
                'minBudget' => 25,
                'recipientsIds' => new List<String>{emp.RecipientId},
                'email' => email.toMap()
            });

            // Step 5: Send gifts
            session.sendGifts(new Map<String, Object>{
                'campaignId' => campaign.Id,
                'recipients' => new Map<String, Object>{
                    'type' => 'ids',
                    'recipientsList' => new List<String>{emp.RecipientId}
                },
                'budget' => new Map<String, Object>{
                    'plan' => 40,
                    'max' => 50,
                    'min' => 25
                },
                'productCollectionId' => birthdayCollection.Id
            });
        }
    }
}

🎯 Example 2: Telegram Bot Integration

// Telegram Bot: Gift Campaign Handler
const TelegramBot = require('node-telegram-bot-api');
const MCPClient = require('./mcp-client');

class SnappyGiftBot {
    constructor(token) {
        this.bot = new TelegramBot(token, { polling: true });
        this.mcpClient = new MCPClient('<https://your-mcp-server.com>');
        this.setupHandlers();
    }

    setupHandlers() {
        // Handle /sendgift command
        this.bot.onText(/\\/sendgift (.+)/, async (msg, match) => {
            const chatId = msg.chat.id;
            const params = this.parseGiftParams(match[1]);

            try {
                // Authenticate user
                await this.mcpClient.signIn(
                    process.env.SNAPPY_USERNAME,
                    process.env.SNAPPY_PASSWORD
                );

                // Execute gift workflow
                const result = await this.executeGiftWorkflow(params);

                this.bot.sendMessage(chatId,
                    `✅ Gift sent successfully!\\n` +
                    `Campaign: ${result.campaignName}\\n` +
                    `Recipient: ${params.recipientEmail}\\n` +
                    `Collection: ${result.collectionName}`
                );

            } catch (error) {
                this.bot.sendMessage(chatId, `❌ Error: ${error.message}`);
            }
        });
    }

    async executeGiftWorkflow(params) {
        // Get collections
        const collections = await this.mcpClient.getAccountCollections(params.accountId);
        const selectedCollection = this.selectBestCollection(collections, params.occasion);

        // Generate personalized content
        const [email, greeting, reveal] = await Promise.all([
            this.mcpClient.createEmail({
                occasion: params.occasion,
                tone: 'professional'
            }),
            this.mcpClient.createGreeting({
                occasion: params.occasion
            }),
            this.mcpClient.chooseReveal({
                occasion: params.occasion
            })
        ]);

        // Create and send campaign
        const campaign = await this.mcpClient.createCampaign({
            companyId: params.companyId,
            accountId: params.accountId,
            collectionId: selectedCollection._id,
            name: `${params.occasion} Gift - ${new Date().toLocaleDateString()}`,
            maxBudget: params.maxBudget,
            minBudget: params.minBudget,
            recipientsIds: [params.recipientId],
            email: JSON.parse(email.content)
        });

        await this.mcpClient.sendGifts({
            campaignId: campaign.created._id,
            recipients: {
                type: 'ids',
                recipientsList: [params.recipientId]
            },
            budget: {
                plan: params.budget,
                max: params.maxBudget,
                min: params.minBudget
            },
            productCollectionId: selectedCollection._id
        });

        return {
            campaignName: campaign.name,
            collectionName: selectedCollection.name
        };
    }
}

// Usage
const bot = new SnappyGiftBot(process.env.TELEGRAM_BOT_TOKEN);

Error Handling

⚠️ Common Error Scenarios

Authentication Errors

{
  "error": {
    "code": "AUTH_FAILED",
    "message": "Login failed",
    "details": "Invalid username or password"
  }
}

Handling:

try {
    await mcpClient.signIn(username, password);
} catch (error) {
    if (error.code === 'AUTH_FAILED') {
        // Prompt for correct credentials
        // Implement retry logic with backoff
    }
}

TOTP Required

{
  "content": [{
    "type": "text",
    "text": "Please enter your TOTP code from your authenticator app"
  }]
}

Handling:

if (response.content[0].text.includes('TOTP code')) {
    const totp = await promptUserForTOTP();
    await mcpClient.signIn(username, password, totp);
}

Collection/Product Not Found

{
  "error": {
    "code": "NOT_FOUND",
    "message": "Collection not found",
    "collectionId": "invalid_collection_id"
  }
}

Budget/Country Restrictions

{
  "error": {
    "code": "NO_PRODUCTS_AVAILABLE",
    "message": "No products available for the specified criteria",
    "details": {
      "budget": 25,
      "country": "US",
      "collectionId": "coll_123"
    }
  }
}

🔄 Retry Strategies

class MCPErrorHandler {
    static async withRetry(operation, maxRetries = 3) {
        for (let attempt = 1; attempt <= maxRetries; attempt++) {
            try {
                return await operation();
            } catch (error) {
                if (attempt === maxRetries || !this.isRetryableError(error)) {
                    throw error;
                }

                const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
                await this.sleep(delay);
            }
        }
    }

    static isRetryableError(error) {
        const retryableCodes = ['NETWORK_ERROR', 'TIMEOUT', 'SERVER_ERROR'];
        return retryableCodes.includes(error.code);
    }

    static sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
}

Best Practices

Security Best Practices

  1. Credential Management

    // ❌ Don't hardcode credentials
    const username = '[email protected]';
    
    // ✅ Use environment variables or secure storage
    const username = process.env.SNAPPY_USERNAME;
    const password = await getSecureCredential('SNAPPY_PASSWORD');
    
  2. Session Management

    // ✅ Implement session reuse
    class MCPSessionManager {
        constructor() {
            this.session = null;
            this.lastAuth = null;
        }
    
        async getValidSession() {
            if (this.isSessionExpired()) {
                this.session = await this.authenticate();
                this.lastAuth = Date.now();
            }
            return this.session;
        }
    }
    

🚀 Performance Optimization

  1. Parallel Execution

    // ✅ Execute independent operations in parallel
    const [email, greeting, reveal] = await Promise.all([
        mcpClient.createEmail(params),
        mcpClient.createGreeting(params),
        mcpClient.chooseReveal(params)
    ]);
    
  2. Caching Strategy

    // ✅ Cache collections data
    class CollectionCache {
        constructor(ttl = 300000) { // 5 minutes
            this.cache = new Map();
            this.ttl = ttl;
        }
    
        async getCollections(accountId) {
            const key = `collections_${accountId}`;
            const cached = this.cache.get(key);
    
            if (cached && (Date.now() - cached.timestamp) < this.ttl) {
                return cached.data;
            }
    
            const collections = await mcpClient.getAccountCollections(accountId);
            this.cache.set(key, { data: collections, timestamp: Date.now() });
            return collections;
        }
    }
    

📝 Logging & Monitoring

class MCPLogger {
    static logToolCall(toolName, params, duration, success) {
        console.log({
            timestamp: new Date().toISOString(),
            tool: toolName,
            parameters: this.sanitizeParams(params),
            duration_ms: duration,
            success: success,
            session_id: this.getSessionId()
        });
    }

    static sanitizeParams(params) {
        // Remove sensitive data
        const sanitized = { ...params };
        delete sanitized.password;
        delete sanitized.totp;
        return sanitized;
    }
}

Troubleshooting

🔍 Debugging Steps

  1. Check Authentication Status

    const authStatus = await mcpClient.checkSignIn();
    console.log('Auth Status:', authStatus);
    
  2. Validate Account Setup

    // Verify account has collections
    const collections = await mcpClient.getAccountCollections(accountId);
    if (collections.length === 0) {
        throw new Error('No collections available. Please set up collections in dashboard.');
    }
    
  3. Test Individual Tools

    // Test each tool independently
    await mcpClient.testTool('get-account-collections', { accountId });
    await mcpClient.testTool('get-collection-products', { collectionId, maxBudget: 100 });
    

📞 Support & Documentation

  • API Documentation: [Internal API Docs]
  • Dashboard Setup: [Dashboard Guide]
  • MCP Protocol: [MCP Specification]
  • Support Contact: [[email protected]]

This integration guide enables external AI systems to seamlessly interact with Snappy's gift campaign management through our MCP server, providing a standardized and secure way to automate personalized gifting workflows.