How I Built Smart Search Across 3,500 Contacts in 2 Hours
Built semantic CRM search with Qdrant Cloud and Claude Code. Now instead of filters — I just ask in plain English.
I had accumulated ~3,500 contacts from various Telegram communities. Founders, creators, developers — all in one JSON. Finding anything specific was a nightmare.
Before this, I’d open the file, write jq queries, filter by fields. Want to find “founders in Berlin interested in AI”? Good luck with boolean logic.
Today I built a solution that lets me simply ask: “Find founders in Berlin working on AI.” And get a list back.
What I built
A local MCP server that:
- Takes my CRM (a JSON file of contacts)
- Turns each contact into a vector via embeddings
- Stores everything in Qdrant Cloud
- Lets me search via Claude Desktop in natural language
Now in Claude Desktop I just type:
“Find people similar to this contact”
“Who in my contacts works in marketing and lives in the US?”
“Creators with a large audience”
And I get relevant results. No jq, no filters, no pain.
Stack
- Qdrant Cloud — free tier, 1GB storage
- Embedding API —
text-embedding-3-smallfor embeddings - MCP (Model Context Protocol) — integration with Claude Desktop
- TypeScript — for the MCP server
Architecture
┌─────────────────────────────────────────────────────┐
│ Mac │
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │Claude Desktop│ ───▶ │ MCP server (node) │ │
│ └──────────────┘ └──────────┬───────────┘ │
└───────────────────────────────────┼────────────────┘
│
┌───────────────┴───────────────┐
│ │
▼ ▼
┌───────────────────┐ ┌───────────────────┐
│ Qdrant Cloud │ │ Embedding API │
│ (vectors) │ │ (embeddings) │
└───────────────────┘ └───────────────────┘
The MCP server runs locally, but the data lives in the cloud. That means:
- Fast search (Qdrant is optimized for vectors)
- Accessible from any device
- Free at current data volumes
How I built it
1. Parsing contacts
Collected contacts from several Telegram communities via the MTProto API. Parsed around 8 communities of various types — from technical to creative. Got ~3,500 unique contacts.
2. Data enrichment
From raw username + first_name, I extracted:
- Roles (founder, developer, creator)
- Companies
- Locations
- Interest-based cohorts
After enrichment, ~1,500 contacts had enough data for quality search.
3. Qdrant Cloud
Created a cluster on cloud.qdrant.io — took 2 minutes. The free tier gives 1GB, which is enough for tens of thousands of contacts.
4. Indexing
Uploaded the enriched contacts to Qdrant — ~90 seconds for the full database. Cost of embeddings: less than a cent.
5. MCP server
Wrote an MCP server in TypeScript and connected it to Claude Desktop. The server takes a text query, generates an embedding, and searches for nearest neighbors in Qdrant.
Setup — standard claude_desktop_config.json configuration with three environment variables (API keys and cluster URL).
Restarted Claude Desktop — done.
Why semantic search
Classic search finds exact word matches. Semantic search finds by meaning.
I type: “people who build products”
I find:
- “founder of startup X”
- “building my own project”
- “indie hacker”
- “launching my own service”
Even if the word “product” doesn’t appear anywhere — it finds by meaning. Embeddings encode semantics, not letters.
What’s next
Right now the data is static — after parsing I need to re-index. Planning:
- Automatic re-indexing when the CRM is updated
- Filters — combining semantics with exact conditions (e.g. by role and city)
- Outreach integration — find people similar to those who already replied
Summary
| What | How much |
|---|---|
| Setup time | ~2 hours |
| Contacts in database | ~3,500 |
| Contacts with data | ~1,500 |
| Indexing cost | < $0.01 |
| Qdrant Cloud | free |
Vibe coding in its purest form: instead of writing complex filters, I taught AI to understand my queries. Now the CRM is a conversation, not SQL.