Build a Sales Follow-Up Agent With the Claude Agent SDK

Build a Sales Follow-Up Agent With the Claude Agent SDK

A Norwegian sales manager just told VG his team closed 1,000+ deals in three months using AI agents. He called it “going from cycling to flying a jet.” He didn’t say which tools. So I’ll show you how to build one yourself with the Claude Agent SDK — the same SDK I use to run Koda, my autonomous marketing agent.

This is a working claude agent sdk tutorial you can fork today. No theory. No “imagine if.” Real code, real tools, real scheduling.

What the agent actually does

A sales follow-up agent earns its keep by doing three boring things reliably:

  1. Reads new leads from a CRM or spreadsheet
  2. Decides which ones need a follow-up today based on rules you define
  3. Drafts a personalized message and either sends it or queues it for approval

That’s it. No magic. The boring version is what actually moves revenue.

Here’s the shape of the agent loop:

from claude_agent_sdk import query, ClaudeAgentOptions

options = ClaudeAgentOptions(
    system_prompt=open("system.md").read(),
    allowed_tools=["Read", "Write", "Bash"],
    mcp_servers={
        "airtable": {
            "command": "npx",
            "args": ["-y", "airtable-mcp-server"],
            "env": {"AIRTABLE_API_KEY": os.environ["AIRTABLE_API_KEY"]},
        },
        "gmail": {
            "command": "python3",
            "args": ["-m", "gmail_mcp"],
        },
    },
)

async for message in query(
    prompt="Run the daily sales follow-up cycle. Read the skill file first.",
    options=options,
):
    print(message)

That’s the whole runtime. The intelligence lives in two places: the system prompt and a skill file. The SDK handles tool calls, retries, and the model loop.

The system prompt is where the personality lives

Your agent needs to know who it is, what it’s allowed to do, and what counts as done. I keep this short and specific.

You are a sales follow-up agent for Acme Co.

Your job: read leads from Airtable, identify who needs a follow-up today,
draft a message in my voice, and queue it as a Gmail draft for my review.

Rules:
- Never send emails directly. Always create drafts.
- Never follow up a lead more than once in 7 days.
- If a lead replied, mark them as active and stop the sequence.
- Log every action to ~/agent/logs/YYYY-MM-DD.md

Voice: direct, concise, first person. No "I hope this finds you well."
No hype words. One specific reason for the outreach. One clear ask.

Notice what’s missing: no chain-of-thought prompts, no “think step by step,” no roleplay. The SDK model is already good at reasoning. What it needs is boundaries and memory.

The skill file is the recipe

The system prompt says what the agent is. The skill file says how to do today’s task. I keep mine in ~/agent/skills/daily-followup.md and reference it from the prompt.

# Daily Follow-Up Skill

## Steps

1. Fetch all Airtable records where `status = "lead"` and `last_contact < 7 days ago = false`.
2. For each lead, check Gmail for any reply from that email address.
3. If replied, update Airtable status to "active" and skip.
4. If not replied, draft a follow-up using the lead's company context.
5. Create a Gmail draft — do NOT send.
6. Log the action.

## Draft template

Subject: Re: {{previous_subject}}

Hi {{first_name}},

Following up on {{specific_thing_from_last_message}}.
{{one_sentence_with_new_angle_or_value}}.

Worth a quick call this week?

— {{sender_name}}

Skills are just markdown. No YAML, no DSL. The agent reads them as context and follows them. I keep a dozen of these for Koda — each covers a single recurring task.

Wire in real tools with MCP

The agent is useless without data. The Claude Agent SDK speaks MCP (Model Context Protocol), which means any MCP server becomes a tool the agent can call. For sales follow-up, I wire in three:

{
  "mcpServers": {
    "airtable": {
      "command": "npx",
      "args": ["-y", "airtable-mcp-server"]
    },
    "gmail": {
      "command": "python3",
      "args": ["-m", "gmail_mcp"]
    },
    "slack": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-slack"]
    }
  }
}

No custom glue code. No webhooks. The agent reads Airtable, reads Gmail, and posts to Slack when it needs approval — all through MCP. This is the piece most tutorials skip, and it’s the piece that makes the agent useful in production.

Schedule it so it runs without you

An agent that only runs when you type a prompt is a chatbot. An agent that runs on a schedule is an employee. I use a simple JSON task file and a daemon that wakes the SDK at the scheduled time:

{
  "sales_followup": {
    "prompt": "Run the daily sales follow-up cycle. Read ~/agent/skills/daily-followup.md first.",
    "cron": "0 9 * * 1-5",
    "type": "approval"
  }
}

type: "approval" means the agent drafts and waits — it posts the drafts to my Slack and I either approve or reject each one. type: "silent" would ship them automatically. For sales outreach I always use approval until I trust the quality for a specific customer segment.

Cost and throughput

One daily run on 50 leads uses roughly 30-80k tokens with Sonnet. That’s under $1 per day per agent. Flaaten’s 1,000 conversions in three months suggests the team runs at least one outreach agent plus a qualification agent and a scheduler. Total SDK cost for that operation: maybe $100/month. The bottleneck is never the model. It’s the quality of your lead list.

What breaks

Three things break in production:

  • Stale skill files. The agent follows the skill exactly. If the skill says “check Airtable” but you moved to HubSpot, it will quietly do nothing. Update the skill, not the prompt.
  • Tool permission drift. The SDK’s allowed_tools list is a hard boundary. If you add a new MCP server and forget to allow it, the agent reports “I don’t have a tool for that” and gives up. Allow explicitly.
  • Approval queue neglect. An approval-mode agent drafts faster than you review. If you don’t review daily, the queue becomes noise and you lose the one message that actually mattered. Either review daily or switch segments to silent.

Why this pattern beats a workflow tool

No canvas. No boxes and arrows. No “here’s how we connect step 3 to step 7.” The Claude Agent SDK is a function call with a system prompt and a toolbox. The intelligence is in the model, the rules are in a markdown file, and the data comes from MCP servers. That’s the whole stack.

I run five of these agents daily for my own work. One for sales outreach, one for content drafting, one for analytics, one for social scheduling, one for community engagement. Total code across all of them: under 500 lines.

I’m documenting the full build process — skills, scheduling, approval queues, and the production patterns that keep them stable — in my Build & Automate community. If you want step-by-step modules with real production code, that’s where it’s happening.

FAQ

Do I need a Claude Max subscription to use the Agent SDK?

No. The SDK works with an API key. You pay per token. For most agents running a few times a day, expect $10-50/month at Sonnet prices. Max is cheaper only if you’re also using Claude Code interactively for hours a day.

Can I use this with a non-Claude model?

The Claude Agent SDK is Claude-specific, but the pattern — system prompt, skill files, MCP tools, scheduled runs — works with any agent framework. I prefer the Claude SDK because the tool-use is more reliable and MCP is natively supported.

How is this different from n8n or Make?

Workflow tools are explicit: every step, every branch, every condition is a box you draw. Agents are declarative: you describe the goal and constraints, and the model decides the path. For rigid processes, workflows win. For anything that requires judgment — “is this lead worth following up today?” — agents win.

Is it safe to let an agent send emails?

Not without approval, not at first. I run every outbound agent in approval mode for at least two weeks per customer segment. Only after the draft quality is boring and consistent do I flip to silent. Shortcuts here cost you the sender reputation.


This post was published using Notipo — my Notion-to-WordPress sync tool. Write in Notion, publish to WordPress automatically.

Similar Posts