Skip to main content
These examples show how AI tools can help you integrate with Emerge more efficiently using the MCP connection.

Example 1: Generate integration code

You ask:
“Generate TypeScript and Python code to create a signed Emerge link and handle the callback”
AI response (with MCP connection): The AI searches the docs, finds the Create Links and Callbacks pages, and generates code that keeps uid server-side (never user input):
import express from 'express';
import crypto from 'crypto';

const app = express();

const CLIENT_ID = process.env.EMERGE_CLIENT_ID;
const SIGNING_SECRET = process.env.EMERGE_SIGNING_SECRET;
const REDIRECT_URI = process.env.EMERGE_REDIRECT_URI || 'https://yourapp.com/emerge/callback';

if (!CLIENT_ID || !SIGNING_SECRET) {
  throw new Error('Missing EMERGE_CLIENT_ID or EMERGE_SIGNING_SECRET');
}

const stateStore = new Map<string, { internalUserId: string; createdAt: number }>();
const uidMap = new Map<string, string>();

function createLinkUrl(internalUserId: string): { url: string; state: string } {
  const timestamp = new Date().toISOString();
  const state = crypto.randomBytes(16).toString('hex');

  const params: Record<string, string> = {
    client_id: CLIENT_ID,
    redirect_uri: REDIRECT_URI,
    state,
    timestamp
  };

  // Use your internal user id or omit uid entirely.
  if (internalUserId) {
    params.uid = internalUserId;
  }

  const signatureBase = Object.keys(params)
    .sort()
    .map(key => `${key}=${params[key]}`)
    .join('&');

  const signature = crypto
    .createHmac('sha256', SIGNING_SECRET)
    .update(signatureBase)
    .digest('hex');

  const finalParams = new URLSearchParams(params);
  finalParams.append('signature', signature);

  return { url: `https://link.emergedata.ai/link/start?${finalParams.toString()}`, state };
}

app.get('/emerge/link', (req, res) => {
  try {
    const internalUserId = String(req.query.user_id || '');
    if (!internalUserId) {
      return res.status(400).send('Missing user_id');
    }

    const { url, state } = createLinkUrl(internalUserId);
    stateStore.set(state, { internalUserId, createdAt: Date.now() });

    return res.redirect(url);
  } catch (err) {
    console.error('Failed to create link', err);
    return res.status(500).send('Server error');
  }
});

app.get('/emerge/callback', async (req, res) => {
  try {
    const { status, state, uid, error_code } = req.query as Record<string, string>;
    const record = stateStore.get(state);

    if (!record) {
      return res.status(400).send('Invalid state');
    }

    stateStore.delete(state);

    if (status === 'success' || status === 'reauthorized') {
      if (!uid) {
        return res.status(400).send('Missing uid');
      }

      uidMap.set(record.internalUserId, uid);
      return res.redirect('/dashboard?connected=true');
    }

    const errorMessage = encodeURIComponent(error_code || 'unknown_error');
    return res.redirect(`/connect?error=${errorMessage}`);
  } catch (err) {
    console.error('Callback error', err);
    return res.status(500).send('Server error');
  }
});

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

Example 2: Understand data availability

You ask:
“What data can I get from Emerge and how soon after consent?”
AI response: The AI finds the Query Overview and Webhooks pages:
  • Data is available after export completes. Poll GET /export/status/{uid} and wait until the required provider in sources[] is data_ready: true.
  • Until a provider is ready, sync queries may return empty results. Handle this gracefully and retry with backoff.

Example 3: Debug a webhook signature issue

You ask:
“My webhook signature verification is failing. What am I doing wrong?”
AI response: The AI checks Webhooks and points out the most common issues:
  1. Raw body required - sign the raw request body, not parsed JSON.
  2. Header name - use the x-signature header.
  3. Comparison - use constant-time comparison to avoid subtle mismatches.
import express from 'express';
import crypto from 'crypto';

const app = express();
app.use(express.raw({ type: 'application/json' }));

const WEBHOOK_SECRET = process.env.EMERGE_WEBHOOK_SECRET;
if (!WEBHOOK_SECRET) {
  throw new Error('Missing EMERGE_WEBHOOK_SECRET');
}

app.post('/webhooks/emerge', async (req, res) => {
  try {
    const signature = req.header('x-signature');
    if (!signature) {
      return res.status(401).send('Missing signature');
    }

    const expected = crypto
      .createHmac('sha256', WEBHOOK_SECRET)
      .update(req.body)
      .digest('hex');

    const signatureBuf = Buffer.from(signature, 'hex');
    const expectedBuf = Buffer.from(expected, 'hex');

    if (signatureBuf.length !== expectedBuf.length || !crypto.timingSafeEqual(signatureBuf, expectedBuf)) {
      return res.status(401).send('Invalid signature');
    }

    const payload = JSON.parse(req.body.toString('utf8'));
    const { event, uid } = payload;

    console.log(`Received ${event} for uid ${uid}`);
    return res.status(200).send('OK');
  } catch (err) {
    console.error('Webhook error', err);
    return res.status(500).send('Server error');
  }
});

app.listen(3001, () => {
  console.log('Webhook listener running on http://localhost:3001');
});

Example 4: Plan a nightly sync

You ask:
“I want to sync Emerge data to my database nightly. What’s the best approach?”
AI response: The AI finds Pagination and Query Overview: Recommended approach:
  • Use delta queries with ingested_begin to fetch new data since the last run.
  • Page with cursor until has_more is false.
  • Store applied_ingested_end as your next ingested_begin.
interface SyncState {
  lastIngestedEnd: string;
}

async function deltaSync(uid: string, token: string, state: SyncState): Promise<SyncState> {
  let cursor: string | undefined;
  let appliedEnd = state.lastIngestedEnd;
  const newData: unknown[] = [];

  try {
    do {
      const params = new URLSearchParams({
        uid,
        ingested_begin: appliedEnd
      });

      if (cursor) {
        params.set('cursor', cursor);
      }

      const response = await fetch(
        `https://query.emergedata.ai/v1/sync/get_search?${params.toString()}`,
        { headers: { Authorization: `Bearer ${token}` } }
      );

      if (!response.ok) {
        const body = await response.text();
        throw new Error(`Query failed (${response.status}): ${body}`);
      }

      const result = await response.json();
      newData.push(...(result.data || []));

      appliedEnd = result.applied_ingested_end || appliedEnd;
      cursor = result.has_more ? result.next_cursor : undefined;
    } while (cursor);

    await processNewData(newData);
    return { lastIngestedEnd: appliedEnd };
  } catch (err) {
    console.error('Delta sync failed', err);
    throw err;
  }
}

async function processNewData(records: unknown[]) {
  console.log(`Processing ${records.length} records`);
}

async function runDeltaSync() {
  const token = process.env.EMERGE_API_TOKEN;
  if (!token) {
    throw new Error('Missing EMERGE_API_TOKEN');
  }

  const state = { lastIngestedEnd: '2024-01-01T00:00:00Z' };
  await deltaSync('psub_d4e5f6789012345678901234abcdef01', token, state);
}

runDeltaSync().catch(err => {
  console.error(err);
  process.exit(1);
});

Example 5: Choose sync vs async for mobile

You ask:
“Should I use sync or async queries for my mobile app?”
AI response: For mobile apps, sync queries are recommended because they return JSON immediately and are simpler to integrate. Your mobile app should call your backend, not the Query API directly.
async function getSearchHistory(uid: string, token: string) {
  try {
    const response = await fetch(
      `https://query.emergedata.ai/v1/sync/get_search?uid=${encodeURIComponent(uid)}`,
      { headers: { Authorization: `Bearer ${token}` } }
    );

    if (!response.ok) {
      const body = await response.text();
      throw new Error(`Query failed (${response.status}): ${body}`);
    }

    return await response.json();
  } catch (err) {
    console.error('Search history query failed', err);
    throw err;
  }
}

async function runQuery() {
  const token = process.env.EMERGE_API_TOKEN;
  if (!token) {
    throw new Error('Missing EMERGE_API_TOKEN');
  }

  const data = await getSearchHistory('psub_d4e5f6789012345678901234abcdef01', token);
  console.log(data);
}

runQuery().catch(err => {
  console.error(err);
  process.exit(1);
});

Example 6: Query user data via MCP

These examples show using the Query API MCP server to directly query consented user data from AI tools. This is different from the documentation MCP - it provides real data access.
Setup required: These examples require the Query API MCP server with valid credentials (auth_token and uid).
Using Claude Code with Query API MCP configured:
claude "Analyze search patterns for psub_c3d4e5f6789012345678901234abcdef from the last month and identify the top 3 interest categories"
What happens:
  1. Claude calls get_search_data tool with date range filter
  2. Receives search history data grouped by category
  3. Analyzes query frequency and patterns
  4. Returns insights with specific examples
Example response:
Based on search history analysis:

1. **Technology/Software** (42% of searches)
   - Frequent queries about React, TypeScript, AWS
   - Pattern: Learning new frameworks

2. **Shopping/Electronics** (28% of searches)
   - Looking at laptop reviews, monitors, keyboards
   - Pattern: Office equipment research

3. **Health/Fitness** (18% of searches)
   - Running training plans, nutrition guides
   - Pattern: Marathon preparation
Using Cursor AI chat with Query API MCP configured:
"Based on this user's YouTube watch history and browsing patterns from the last 2 weeks,
suggest 5 relevant blog topics for our content calendar"
What happens:
  1. AI calls get_youtube_data and get_browsing_data tools
  2. Identifies themes and recurring interests
  3. Cross-references patterns across data sources
  4. Generates topic suggestions with rationale
Example response:
Recommended blog topics based on user data:

1. "Getting Started with Docker in 2024"
   - Watched 12 Docker tutorials, visited Docker docs 8 times

2. "Best Practices for React State Management"
   - Heavy focus on Redux and Zustand content

3. "Home Office Setup Guide for Developers"
   - Browsed ergonomic furniture, watched office tour videos

4. "Debugging Node.js Performance Issues"
   - Searched for profiling tools, read performance articles

5. "AI Tools for Code Review"
   - Interest in GitHub Copilot, Claude Code, and similar tools
Using Claude Desktop with Query API MCP:
"Create a user activity summary for the last 7 days showing:
- Most searched topics
- YouTube categories watched
- Top browsing domains
- Peak activity times"
What happens:
  1. AI calls multiple MCP tools in parallel (get_search_data, get_youtube_data, get_browsing_data)
  2. Aggregates and analyzes data across sources
  3. Identifies patterns and trends
  4. Formats results as structured summary
Example response:
User Activity Summary (Last 7 Days)

📊 Search Activity
- 127 total searches
- Top topics: Machine Learning (23), JavaScript (18), AWS (15)
- Peak search time: 10am-12pm EST

🎥 YouTube Activity
- 34 videos watched (8.2 hours)
- Top categories: Technology (41%), Education (32%), Entertainment (27%)
- Longest session: 2.5 hours on Sunday

🌐 Browsing Activity
- 412 page views
- Top domains: github.com (87), stackoverflow.com (64), docs.aws.amazon.com (42)
- Most active day: Wednesday (94 pages)
Using Claude Code to generate sync script:
claude "Write a Python script that uses the MCP tools to sync all new search data since my last sync timestamp into my data warehouse, handling pagination automatically"
What happens:
  1. Claude accesses Query API MCP tool documentation
  2. Generates script using get_search_data with delta query parameters
  3. Implements pagination with cursor and has_more handling
  4. Uses ingested_begin for incremental updates
Claude will generate a complete script that:
  • Loads last sync timestamp from state file
  • Calls MCP tool with ingested_begin parameter
  • Pages through all results
  • Saves applied_ingested_end for next sync

Try it yourself

For documentation help (Docs MCP):

  • “How do I implement retry logic for Emerge API calls?”
  • “What happens if a user revokes consent?”
  • “Generate a React component that shows a Connect Data button”
  • “Show the correct way to handle uid without asking the user”
See MCP Docs Setup to connect your AI tool.

For querying user data (Query API MCP):

  • “Show me search patterns for the last week”
  • “What categories is this user most interested in?”
  • “Compare browsing and YouTube activity to find common interests”
  • “Generate a weekly activity report”
See Query API MCP Setup to connect with credentials.