Back to blog
EngineeringMarch 5, 20256 min read

Building AI Agents with CRM Data: A Practical Guide

TnT

The nodestash Team

Engineering

aimcpagentsllmcrm-api

The Problem: AI Agents Without Context

AI agents are only as useful as the context they have access to. A support agent that can't look up a customer's history is just a chatbot. A sales assistant that can't see deal stages is just GPT with a sales prompt.

The missing piece is structured data access. LLMs can reason and generate text. What they can't do is query databases, look up customer records, or check deal pipelines — unless you give them tools.

CRM Data as Agent Context

Customer data is some of the most valuable context an AI agent can have:

  • Who is this person? Contact details, company, role, tags
  • What's their history? Activities, emails, meetings, notes
  • Where are they in the pipeline? Deal stage, value, expected close date
  • What's custom to them? Lead score, source, industry, plan

Traditional CRMs make this data hard to access programmatically. Rate limits are tight, APIs are inconsistent, and you end up building a caching layer just to give your agent basic lookup capabilities.

Two Approaches

Approach 1: Direct API Integration

The straightforward way. Your agent has tools that call the nodestash API directly.

import { NodeStash } from '@nodestash/sdk'

const crm = new NodeStash({ apiKey: process.env.NODESTASH_KEY })

// Define tools the agent can use
const tools = [
  {
    name: 'lookup_contact',
    description: 'Look up a contact by email address',
    parameters: {
      email: { type: 'string', description: 'Email address to search for' },
    },
    execute: async ({ email }: { email: string }) => {
      const contacts = await crm.contacts.list({ search: email })
      if (!contacts.data.length) return { found: false }

      const contact = contacts.data[0]
      return {
        found: true,
        id: contact.id,
        name: `${contact.attributes.first_name} ${contact.attributes.last_name}`,
        email: contact.attributes.email,
        company: contact.attributes.company_id,
        tags: contact.attributes.tags,
      }
    },
  },
  {
    name: 'get_deal_status',
    description: 'Get the current deals for a contact',
    parameters: {
      contact_id: { type: 'string', description: 'Contact ID' },
    },
    execute: async ({ contact_id }: { contact_id: string }) => {
      const deals = await crm.deals.list({ contact_id })
      return deals.data.map(deal => ({
        title: deal.attributes.title,
        value: deal.attributes.value,
        stage: deal.attributes.stage_id,
        expected_close: deal.attributes.expected_close_date,
      }))
    },
  },
  {
    name: 'log_interaction',
    description: 'Log an AI-assisted interaction as an activity',
    parameters: {
      contact_id: { type: 'string' },
      subject: { type: 'string' },
      body: { type: 'string' },
    },
    execute: async (params: { contact_id: string; subject: string; body: string }) => {
      return await crm.activities.create({
        type: 'note',
        subject: params.subject,
        body: params.body,
        contact_id: params.contact_id,
        is_done: true,
      })
    },
  },
]

This works well when you're building a custom agent framework. You define tools, pass them to the LLM, and handle tool calls.

Approach 2: MCP Server (Model Context Protocol)

MCP is a standard protocol for connecting AI models to data sources. nodestash ships an MCP server that exposes all CRM operations as tools that any MCP-compatible client can use.

// claude_desktop_config.json
{
  "mcpServers": {
    "nodestash": {
      "command": "npx",
      "args": ["@nodestash/mcp"],
      "env": {
        "NODESTASH_API_KEY": "nds_live_..."
      }
    }
  }
}

Once configured, your AI client (Claude, Cursor, or any MCP-compatible tool) can:

  • Search and retrieve contacts
  • Create and update deals
  • Log activities and notes
  • Manage pipeline stages
  • Query custom fields

No custom integration code. The MCP server translates between the AI model and the nodestash API.

Real-World Patterns

Pattern 1: Customer Support Agent

An AI support agent that looks up customer context before responding.

async function handleSupportTicket(ticket: Ticket) {
  // 1. Look up the customer
  const context = await tools.lookup_contact({ email: ticket.from_email })

  // 2. Get their deal/subscription info
  const deals = context.found
    ? await tools.get_deal_status({ contact_id: context.id })
    : []

  // 3. Get recent interaction history
  const activities = context.found
    ? await crm.activities.list({
        contact_id: context.id,
        limit: 5,
      })
    : { data: [] }

  // 4. Build context for the LLM
  const systemPrompt = `You are a support agent. Customer context:
    Name: ${context.name}
    Plan: ${context.tags?.includes('enterprise') ? 'Enterprise' : 'Standard'}
    Active deals: ${deals.length}
    Recent interactions: ${activities.data.length}
    Last contact: ${activities.data[0]?.attributes.created_at ?? 'N/A'}`

  // 5. Generate response with context
  const response = await llm.complete({
    system: systemPrompt,
    messages: [{ role: 'user', content: ticket.body }],
  })

  // 6. Log the AI interaction
  if (context.found) {
    await tools.log_interaction({
      contact_id: context.id,
      subject: `AI Support: ${ticket.subject}`,
      body: `Query: ${ticket.body}\n\nAI Response: ${response}`,
    })
  }

  return response
}

Pattern 2: Sales Intelligence

An agent that enriches leads and suggests next actions.

async function analyzeLead(contactId: string) {
  const contact = await crm.contacts.get(contactId)
  const deals = await crm.deals.list({ contact_id: contactId })
  const activities = await crm.activities.list({ contact_id: contactId })

  const analysis = await llm.complete({
    system: 'You are a sales analyst. Analyze this lead and suggest next actions.',
    messages: [{
      role: 'user',
      content: JSON.stringify({
        contact: contact.attributes,
        deals: deals.data.map(d => d.attributes),
        activity_count: activities.data.length,
        last_activity: activities.data[0]?.attributes,
      }),
    }],
  })

  // Auto-tag based on analysis
  await crm.contacts.update(contactId, {
    tags: [...contact.attributes.tags, 'ai-analyzed'],
    custom_fields: {
      ai_summary: analysis,
      analyzed_at: new Date().toISOString(),
    },
  })

  return analysis
}

Pattern 3: Automated Data Entry

An agent that processes unstructured data and creates CRM records.

async function processBusinessCard(imageUrl: string) {
  // Use vision model to extract contact info
  const extracted = await llm.complete({
    messages: [{
      role: 'user',
      content: [
        { type: 'image_url', image_url: { url: imageUrl } },
        { type: 'text', text: 'Extract: name, email, phone, company, title. Return JSON.' },
      ],
    }],
  })

  const data = JSON.parse(extracted)

  // Create or update contact in CRM
  const existing = await crm.contacts.list({ search: data.email })

  if (existing.data.length > 0) {
    return await crm.contacts.update(existing.data[0].id, {
      phone: data.phone,
      title: data.title,
    })
  }

  return await crm.contacts.create({
    first_name: data.first_name,
    last_name: data.last_name,
    email: data.email,
    phone: data.phone,
    title: data.title,
    tags: ['business-card-scan'],
  })
}

Why a CRM API Matters for AI

The key insight: AI agents need structured, queryable data. Not a CSV export. Not a data dump. A real-time API with search, filtering, and the ability to write back.

nodestash was designed for this from day one:

  • Fast search — Meilisearch-powered full-text search with typo tolerance
  • Custom fields — Store AI-generated metadata directly on CRM records
  • Activity logging — Track every AI interaction for audit and training
  • Webhooks — Trigger AI workflows when CRM data changes
  • Rate limits that make sense — Up to 500 requests/second on Enterprise, with clear headers

Getting Started

  1. Sign up for nodestash (free tier available)
  2. Install the SDK: npm install @nodestash/sdk
  3. Or set up the MCP server: npx @nodestash/mcp
  4. Start building your agent

The free tier gives you 100 contacts and 1,000 API calls/month — enough to prototype your agent and validate the workflow before scaling up.

Ready to build your CRM?

Get started with nodestash in minutes. No credit card required.