I Built an MCP Server So I'd Never Have to Manually Import Excel Data Again
Source: Dev.to
The Problem That Started It All
Picture this: you’re running a small café. You’ve got your menu in an Excel spreadsheet (because that’s what everyone uses, right?). Now you need to get that data into MongoDB for your new web app.
Your options
- Copy‑paste each item manually (soul‑crushing)
- Write a one‑off Node.js script (works once, breaks next time)
- Ask ChatGPT to write the script (gets you 80 % there, then you’re on your own)
I wanted option 4: Talk to Claude like a human and have it just… work.
That’s where MCP (Model Context Protocol) comes in.
What Even Is MCP?
MCP is basically a way to give Claude (or any AI) super‑powers. Instead of Claude just answering questions, it can actually do things—like reading files, calling APIs, or, in my case, importing Excel data into databases.
| Without MCP | With MCP |
|---|---|
| Claude is a really smart friend who can only give advice. | Claude is a really smart friend who can actually SSH into your server and fix things. |
The catch? You have to build the “server” that does the actual work.
Building MindfulMapper
The Vision
I wanted to be able to say:
“Hey Claude, import menu.xlsx into my products collection. Map ‘Name (EN)’ →
name.enand ‘Name (TH)’ →name.th. Oh, and auto‑generate IDs with prefix ‘spb’.”
…and have it just work.
The Reality (aka Pain Points)
Pain Point #1 – The Dotenv Disaster
My first version used dotenv to load environment variables:
import dotenv from 'dotenv';
dotenv.config();
dotenv prints a helpful message to stdout:
[dotenv@17.2.4] injecting env (4) from .env
Claude Desktop saw this message, tried to parse it as JSON (because MCP uses JSON‑RPC), and promptly died. Took me WAY too long to figure this out.
Solution: suppress the message or hard‑code the env vars in the Claude Desktop config. I went with the latter.
Pain Point #2 – SDK Version Hell
The MCP SDK evolves fast—really fast. Version 1.26.0 uses completely different syntax than the examples online.
What the examples showed (pre‑1.26.0):
server.addTool({
name: "my_tool",
description: "Does a thing",
parameters: z.object({ /* … */ }),
execute: async ({ /* … */ }) => { /* … */ }
});
What actually works in v1.26.0:
const server = new Server(
{ name: "my-server", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [/* … */]
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
// Handle tool calls
});
Completely different. Spent hours on this one.
Pain Point #3 – Auto‑Generating IDs
I wanted IDs like spb-0001, spb-0002, etc. The trick is maintaining a counter in MongoDB:
async function getNextId(prefix = 'spb') {
const counterCollection = db.collection('counters');
const result = await counterCollection.findOneAndUpdate(
{ _id: 'item_id' },
{ $inc: { seq: 1 } },
{ upsert: true, returnDocument: 'after' }
);
const num = result.seq || 1;
return `${prefix}-${String(num).padStart(4, '0')}`;
}
This ensures:
- No duplicate IDs (even if you import the same file multiple times)
- Sequential numbering
- Custom prefixes for different item types
The Cool Parts
1. Flexible Column Mapping
Want to map Excel columns to nested MongoDB objects? Easy:
// Excel columns: "Name (EN)", "Name (TH)"
// Mapping: { "name.en": "Name (EN)", "name.th": "Name (TH)" }
// Result in MongoDB:
{
id: "spb-0001",
name: {
en: "Americano",
th: "อเมริกาโน่"
}
}
The mapper handles the dot‑notation automatically.
2. Natural Language Interface
Instead of writing code every time, I just tell Claude:
“Import menu.xlsx into products collection. Use prefix ‘spb’. Clear existing data.”
Claude translates that into the right MCP tool call with the correct parameters. It’s like having a very patient assistant who never gets tired of your data imports.
3. It Actually Works in Production
I’m using this with MongoDB Atlas (cloud) for a real café menu system. The fact that it works reliably enough for production still surprises me.
How to Use It
1. Install
git clone https://github.com/kie-sp/mindful-mapper.git
cd mindful-mapper
npm install
2. Configure Claude Desktop
Add the following to ~/Library/Application Support/Claude/claude_desktop_config.json (adjust the paths/values to your environment):
{
"mcpServers": {
"mindful-mapper": {
"command": "node",
"args": ["/full/path/to/mindful-mapper/upload-excel.js"],
"env": {
"MONGODB_URI": "your-mongodb-connection-string",
"MONGODB_DB_NAME": "your_db",
"ID_PREFIX": "spb"
}
}
}
}
3. Use It
-
Restart Claude Desktop.
-
Say, for example:
“Import /path/to/menu.xlsx into products collection. Use prefix ‘spb’.”
That’s it.


Lessons Learned
1. MCP is Still Early Days
The SDK is changing fast. Code that worked two months ago might break today. Always check the version you’re using.
2. Debugging MCP Servers Is… Interesting
When your server crashes silently you won’t see a stack trace in Claude. Your best friend becomes:
node your-server.js 2>&1
and the logs located at:
~/Library/Logs/Claude/mcp*.log
3. The Payoff Is Worth It
Once it works, it’s magic. I went from dreading data imports to actually enjoying them (well, at least not dreading them).
What’s Next?
Current limitations I want to fix:
- No schema validation – It will happily import garbage data.
- No update/upsert mode – Only “insert” or “clear‑and‑insert”.
- MongoDB only – PostgreSQL support exists but needs love.
- No multi‑sheet support – Only the first sheet is processed.
But honestly, for ~90 % of my use cases it works perfectly as‑is.
Try It Yourself
- GitHub:
- Requirements: Node.js 18+, MongoDB, Claude Desktop
- License: MIT (do whatever you want with it)
If you build something cool with it, let me know! Or if you hit the same pain points I did, at least now you know you’re not alone.
Final Thoughts
Building MCP servers is weird. It’s not quite backend development, not quite AI engineering – it’s a new kind of tool‑building for an AI to use on your behalf.
When it works, you can casually tell Claude to handle your data imports while you go make coffee. That’s pretty cool.
If you end up using this or building something similar, I’d love to hear about it! Feel free to open an issue on GitHub or reach out.
Happy importing! 🎉
Built with: Node.js, MongoDB, MCP SDK, and an unreasonable amount of trial and error