Content
# LeadMind MCP
> An MCP (Model Context Protocol) server that turns an AI lead management system into a conversational tool any MCP-compatible AI client (like Claude Desktop) can control directly.
**Free-tier only. Production-grade reliability. Built for a public GitHub demo.**
LeadMind demonstrates four things at once:
1. **Practical MCP implementation** using all three MCP primitives — **tools**, **resources**, and **prompts** — with the official Python SDK.
2. **Agentic reasoning beyond CRUD** — the server classifies, suggests next actions, and synthesizes weekly reviews, not just stores records.
3. **Production-grade reliability engineering** — caching, graceful degradation, and rate-limit handling, all of which are underrated but highly valued skills.
4. **Zero-cost architecture** — every layer of the stack runs on a genuinely free tier (Groq LLM, SQLite, no external paid services). Suitable for a public GitHub demo with unlimited concurrent visitors.
---
## Table of Contents
- [Architecture](#architecture)
- [The Free-Tier-Only Stack](#the-free-tier-only-stack)
- [Setup](#setup)
- [Connecting to Claude Desktop](#connecting-to-claude-desktop)
- [MCP Primitives Reference](#mcp-primitives-reference)
- [Tools (8)](#tools-8)
- [Resources (2)](#resources-2)
- [Prompt Templates (1)](#prompt-templates-1)
- [Demo-Safety Design](#demo-safety-design)
- [Project Structure](#project-structure)
- [Optional Integrations](#optional-integrations-n8n--gmail)
- [Demo Walkthrough](#demo-walkthrough)
- [Troubleshooting](#troubleshooting)
- [License](#license)
---
## Architecture
```
┌────────────────────────────────────────────────────────────────────────────┐
│ MCP CLIENT (e.g. Claude Desktop) │
│ │
│ User says: "Show me all hot leads and suggest next action for the top." │
└──────────────────────────────┬─────────────────────────────────────────────┘
│ MCP protocol (JSON-RPC over stdio or SSE)
▼
┌────────────────────────────────────────────────────────────────────────────┐
│ LEADMIND MCP SERVER │
│ (mcp_server.py, FastMCP) │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────────────────────────┐ │
│ │ 8 Tools │ │ 2 Resources │ │ 1 Prompt Template │ │
│ │ (tools.py) │ │ │ │ (weekly_lead_review) │ │
│ └──────┬──────┘ └──────┬──────┘ └───────────────┬──────────────┘ │
│ │ │ │ │
│ └────────────────┬┴──────────────────────────┘ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ Reliability Layer │ │
│ │ │ │
│ │ ┌────────────┐ ┌────────────────────┐ ┌──────────────┐ │ │
│ │ │ cache.py │───▶│ groq_classifier.py │───▶│ fallback_ │ │ │
│ │ │ TTL+LRU │ │ (Groq + counter) │ │ classifier │ │ │
│ │ └────────────┘ └─────────┬──────────┘ └──────────────┘ │ │
│ │ │ rate-limit / error │ │
│ │ ▼ → fallback │ │
│ └───────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────┬─────────────────────────────────────────────┘
│
┌──────────────┴───────────────┐
▼ ▼
┌─────────────────────────┐ ┌──────────────────────────────────┐
│ SQLite (leadmind.db) │ │ Groq API (free tier) │
│ │ │ Llama-3.3-70b-versatile │
│ - leads │ │ 30 req/min free │
│ - lead_history │ └──────────────────────────────────┘
│ - audit_log │
│ - meta │
└─────────────────────────┘
```
**Data flow for `classify_lead(text)`:**
```
classify_lead(text)
│
▼
cache.get() ────────hit─────────▶ return cached (source="cache")
│
miss
│
▼
Groq API call ─────success──────▶ return LLM result (source="groq")
│
429 / timeout / error
│
▼
fallback_classifier.classify() ──▶ return rule-based (source="fallback")
│
▼
cache.set() (so subsequent identical calls are free)
```
---
## The Free-Tier-Only Stack
Every component runs on a genuinely free tier. **No credit-card trials, no paid APIs, no surprise bills.**
| Layer | Choice | Free-tier details |
|---|---|---|
| LLM | **Groq API** (`llama-3.3-70b-versatile`) | Free forever; ~30 req/min, 14,500 req/day. [Get a key](https://console.groq.com/keys). |
| Database | **SQLite** (local file) | Zero cost, zero ops, zero hosting. Lives in `leadmind.db`. |
| MCP SDK | **`modelcontextprotocol/python-sdk`** | Open-source (MIT). |
| Web framework | **FastAPI** (optional, webhook only) | Open-source. |
| Transport | **stdio** (local) or **SSE** (remote) | Built into the MCP SDK. |
| Hosting | Any free host (Render, Fly.io, Railway free tier, your laptop) | Static process; no special infra. |
**What's intentionally NOT used:**
- OpenAI / Anthropic APIs (paid, even with free credits they require a card).
- Hosted Postgres / Supabase / PlanetScale (free tiers exist but require sign-up + card).
- Any external SaaS for caching, queueing, or monitoring.
---
## Setup
### Prerequisites
- Python 3.10+
- (Optional) A free Groq API key from <https://console.groq.com/keys>
### Install
```bash
git clone <your-fork-url>
cd leadmind-mcp
python -m venv .venv
source .venv/bin/activate # on Windows: .venv\Scripts\activate
pip install -r requirements.txt
```
### Configure
```bash
cp .env.example .env
# Edit .env and optionally fill in GROQ_API_KEY
```
The server works **without** a Groq key — it will use the rule-based fallback classifier for everything. The Groq key just upgrades the classification quality.
### Run
```bash
# Default: stdio transport (for Claude Desktop)
python mcp_server.py
# Or: SSE transport (for remote clients)
MCP_TRANSPORT=sse MCP_PORT=8080 python mcp_server.py
```
On first run, the server:
1. Creates `leadmind.db` (SQLite).
2. Seeds it with 18 realistic demo leads.
3. Starts listening on stdio (or SSE).
---
## Connecting to Claude Desktop
1. Open Claude Desktop → **Settings → Developer → Edit Config**.
2. Add LeadMind to `claude_desktop_config.json`:
```json
{
"mcpServers": {
"leadmind": {
"command": "python",
"args": ["/absolute/path/to/leadmind-mcp/mcp_server.py"],
"env": {
"GROQ_API_KEY": "your_key_here_or_leave_empty",
"DEMO_MODE": "true"
}
}
}
}
```
(See `claude_desktop_config.example.json` for a template.)
3. Restart Claude Desktop. You should see `leadmind` under **Settings → Developer → MCP Servers** with a green "connected" indicator.
4. Try the demo script in [`demo_script.md`](./demo_script.md).
---
## MCP Primitives Reference
### Tools (8)
| # | Tool | Description | Example call |
|---|---|---|---|
| 1 | `tool_get_leads(status="")` | Returns all leads, optionally filtered by Hot/Warm/Cold/Converted/Lost. | `tool_get_leads(status="Hot")` |
| 2 | `tool_classify_lead(text)` | Classifies a message into Hot/Warm/Cold with confidence + reasoning. Uses cache → Groq → fallback. | `tool_classify_lead("Need a CRM urgently, budget approved")` |
| 3 | `tool_add_lead(name, contact_info, message, source="manual")` | Adds a lead and auto-classifies on insert. | `tool_add_lead(name="John", contact_info="john@x.com", message="...", source="Website Form")` |
| 4 | `tool_update_lead_status(id, status)` | Manually overrides status; logs change with timestamp. | `tool_update_lead_status(id=1, status="Converted")` |
| 5 | `tool_get_lead_stats()` | Aggregate stats: totals, breakdown by status/source, avg response time, conversion rate, Groq usage. | `tool_get_lead_stats()` |
| 6 | `tool_get_lead_history(id)` | Full timeline of a lead's status changes and interactions. | `tool_get_lead_history(id=1)` |
| 7 | `tool_suggest_next_action(id)` | AI-generated next-action recommendation (with fallback). | `tool_suggest_next_action(id=1)` |
| 8 | `tool_bulk_import_leads(csv_data)` | Parses CSV text and batch-classifies + inserts. Uses cache to avoid redundant API calls on duplicates. | `tool_bulk_import_leads("name,message\nJohn,...")` |
#### Example tool response (`tool_classify_lead`)
```json
{
"status": "Hot",
"confidence": 0.85,
"reasoning": "Hot because urgent language detected and budget mentioned — prioritize outreach.",
"source": "groq"
}
```
### Resources (2)
Resources are read-only data the MCP client can "read" like a file.
| URI | Description |
|---|---|
| `leads://dashboard` | Live-updating summary snapshot of the whole pipeline — totals, breakdown, recent leads, Groq usage monitoring. |
| `audit://recent` | Last 30 tool-call audit entries with which path was used (groq/fallback/cache). Demonstrates observability. |
### Prompt Templates (1)
| Prompt | Description |
|---|---|
| `weekly_lead_review` | Pre-packages a structured prompt for the AI client to generate a full weekly performance summary. Auto-injects current pipeline stats and instructs the AI to fetch more details via the tools. |
---
## Demo-Safety Design
This project is built to be deployed publicly on GitHub with a live demo link. Multiple strangers may open and test it simultaneously. The system **must not break, get rate-limited into failure, or run out of quota**. Here's how each risk is mitigated:
### 1. Response Caching (rate-limit prevention)
- `cache.py` implements a thread-safe TTL cache (5-minute default, 500 entries max).
- Cache key is a SHA-256 hash of the normalized message text.
- Identical `classify_lead(text)` calls within the TTL window return cached results — **zero Groq API calls burned**.
- Bulk imports of similar messages also benefit (a common pattern in real CRM data).
- The cache is shared across all tool calls in the process.
- Cache hits are tagged `source: "cache"` so they're visible in the audit log.
### 2. Graceful Fallback (zero-downtime under load)
- `fallback_classifier.py` implements a transparent keyword/heuristic classifier with the same input/output contract as the Groq path.
- If Groq returns 429 (rate limit), 5xx, times out, or any other error — the request transparently falls back to the rule-based classifier.
- The response always includes `source: "groq" | "fallback" | "cache"` so the caller knows which path was used.
- The fallback is **explainable**: every decision returns a human-readable reasoning string like `"Hot because 3 urgent/intent signal(s) detected (budget, asap, decision-ready) — prioritize outreach."`.
### 3. Seeded Demo Data (no empty-state embarrassment)
- `seed_data.py` defines 18 realistic demo leads spanning Hot/Warm/Cold and 8 different sources.
- On first run, the DB is seeded automatically.
- Every seed lead's message is written to exercise both the Groq classifier and the fallback (so the demo is interesting even without a Groq key).
### 4. Demo Mode Auto-Reset (self-healing dataset)
- When `DEMO_MODE=true` (default), the DB is reset to seed data every 6 hours (configurable via `DEMO_RESET_INTERVAL_SEC`).
- Reset is triggered lazily on the next tool call after the interval expires — no background threads required.
- The reset preserves the `audit_log` table so observability data accumulates across resets.
- This means: even if 50 visitors add junk leads, call `tool_bulk_import_leads` with garbage, or `tool_update_lead_status` on everything — the demo self-heals within hours.
### 5. Groq Usage Monitoring (free-tier visibility)
- Every Groq call increments an in-process counter (`groq_classifier.get_call_count()`).
- Every call is also logged to `groq_usage.log` with status (`success`, `rate_limited`, `error`).
- The `leads://dashboard` resource shows: calls this session, total logged, rate-limited count, error count.
- `tool_get_lead_stats()` returns a `groq_usage` block in its response.
- The `audit_log` table records every tool call with flags: `used_groq`, `used_fallback`, `used_cache` — so you can answer "what fraction of my demo traffic used the LLM?" with a single SQL query.
### 6. Optional Authentication (not just a local toy)
- Set `LEADMIND_AUTH_ENABLED=true` and `LEADMIND_API_KEY=<secret>` for SSE / webhook deployments.
- For stdio (Claude Desktop), auth is typically off because the transport is local.
- The webhook receiver (`webhook_receiver.py`) validates `X-API-Key` on every request.
---
## Project Structure
```
leadmind-mcp/
├── mcp_server.py # FastMCP server: registers tools, resources, prompts
├── tools.py # 8 MCP tool implementations (with audit logging)
├── db.py # SQLite schema, CRUD, audit, demo-reset logic
├── groq_classifier.py # Groq API + caching + fallback chain + usage counter
├── fallback_classifier.py # Rule-based keyword classifier (negation-aware, weighted)
├── cache.py # Thread-safe TTL+LRU cache
├── seed_data.py # 18 realistic demo leads + idempotent seeder
├── webhook_receiver.py # Optional FastAPI app for n8n/Gmail integration
├── config.py # Central env-driven configuration
├── test_leadmind.py # End-to-end tool tests (no Groq key needed)
├── test_mcp_protocol.py # Real MCP JSON-RPC protocol smoke test
├── Makefile # install / test / run shortcuts
│
├── requirements.txt # Python deps (all free / open-source)
├── .env.example # Environment variable template
├── .gitignore
├── LICENSE # MIT
├── claude_desktop_config.example.json
├── demo_script.md # Example conversation flow walkthrough
└── README.md # (this file)
```
---
## Optional Integrations (n8n + Gmail)
### n8n Webhook Trigger
`webhook_receiver.py` exposes an HTTP endpoint that n8n workflows can POST to:
```bash
# Start the webhook receiver (separate process from MCP server)
pip install fastapi uvicorn
python webhook_receiver.py # listens on :8000
```
In n8n, add an **HTTP Request** node that POSTs to `http://your-host:8000/webhook/lead`:
```json
{
"name": "Lead from n8n",
"contact_info": "lead@example.com",
"message": "Interested in your product, saw it on the webinar",
"source": "n8n Workflow"
}
```
The webhook receiver uses the same `tools.add_lead()` function, so the lead is auto-classified and inserted with full history.
### Gmail Notification Hook (free tier)
Gmail's free tier allows ~20k emails/day — plenty for a demo. The recommended pattern:
1. Create a Gmail filter that forwards matching emails (e.g. subject contains "Lead Inquiry") to a Gmail Apps Script.
2. The Apps Script parses the email and POSTs to your `/webhook/lead` endpoint.
3. (Optional) Set `GMAIL_NOTIFY_TO` env var to enable outbound notifications on new Hot leads.
The actual Gmail API integration is intentionally not bundled — it would require OAuth setup that breaks the zero-config demo. The webhook contract is documented so anyone can plug in their own Gmail bridge.
---
## Demo Walkthrough
See [`demo_script.md`](./demo_script.md) for a full conversation-flow walkthrough. The short version:
1. **"Read the leads://dashboard resource."** → Live pipeline snapshot.
2. **"Show me all hot leads and suggest next action for the top one."** → Two tool calls chained automatically.
3. **"Add a new lead: John Smith, john@smith.com, 'Need CRM urgently, budget approved, this week', Cold Outreach."** → Auto-classified as Hot.
4. **"Bulk import these CSV leads..."** → Bulk path, cached classification.
5. **"Mark lead id=1 as Converted."** → Manual override, logged in history.
6. **"Generate the weekly_lead_review prompt."** → Template auto-injects stats; Claude calls further tools as instructed.
---
## Testing
Two test suites are included. Both run **without a Groq API key** (they exercise the fallback path), so they're safe to run in CI.
### 1. End-to-end tool tests
```bash
make test # or: python test_leadmind.py
```
Calls every tool directly (bypassing MCP transport), verifies the cache hit on second `classify_lead` call, asserts that `add_lead` auto-classifies correctly, and checks that resources/prompts return expected content.
### 2. Real MCP protocol smoke test
```bash
make test-proto # or: python test_mcp_protocol.py
```
Spawns `mcp_server.py` as a subprocess, speaks the actual MCP JSON-RPC protocol over stdio, and verifies:
- `initialize` handshake succeeds
- `tools/list` returns 8 tools
- `resources/list` returns 2 resources
- `prompts/list` returns 1 prompt
- `tools/call` returns real lead data
- `resources/read` returns the dashboard text
- `prompts/get` returns the weekly review template
This proves the server is connectable from any real MCP client (Claude Desktop, Cursor, etc.), not just callable as Python functions.
---
## Troubleshooting
**Server doesn't connect in Claude Desktop**
- Check the path in `claude_desktop_config.json` is absolute.
- Check `python` is on your PATH (or use the full path to the Python binary).
- Restart Claude Desktop fully (not just reload).
- Look at Claude Desktop's developer logs: `~/Library/Logs/Claude/mcp-server-leadmind.log` (macOS).
**`ModuleNotFoundError: No module named 'mcp'`**
- Activate your virtualenv: `source .venv/bin/activate`.
- Reinstall: `pip install -r requirements.txt`.
**Classifications always say `source: "fallback"`**
- Either `GROQ_API_KEY` is not set, or Groq is rate-limiting you.
- Check `groq_usage.log` — if you see lots of `rate_limited` lines, the fallback is doing its job.
- The fallback is fully functional; classifications are still correct, just less nuanced.
**Database is empty after cloning**
- Run `python mcp_server.py` once to trigger initial seeding.
- Or run `python -c "from seed_data import seed_database; seed_database()"`.
**Demo mode keeps resetting my changes**
- That's intentional. Set `DEMO_MODE=false` in `.env` if you want your changes to persist.
---
## License
MIT — see [`LICENSE`](./LICENSE).
---
## Why this project exists
Built as a portfolio piece for an **AI Automation Developer / Multi-Agent Systems** role. It demonstrates:
- **MCP mastery** — all three primitives (tools, resources, prompts) used appropriately.
- **Agentic design** — the server doesn't just CRUD; it classifies, recommends, and synthesizes.
- **Reliability engineering** — caching, graceful degradation, rate-limit handling. This is the part most portfolio projects skip, and the part hiring managers care about most for production roles.
- **Cost discipline** — a real, usable AI product running on $0/month of infrastructure, suitable for unlimited public testing.
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available