Claude MCP in 12 Minutes: The Config Layer Nobody Teaches

If you've spent the year learning Claude prompts but your Claude still can't read a single file on your machine, you're missing the actual unlock. Prompts are maybe 20% of the value. The other 80% is what Claude is wired into — your filesystem, your database, your inbox, your Telegram. And the only thing standing between "smart chatbot in a tab" and "operator that runs your business" is one JSON file most people don't know exists.
I'm going to walk through that file, the 6 MCP servers I run daily on real client work, the three mistakes that broke my setup the first month, and the live workflow where Claude reconciles invoices and drafts follow-ups in about 45 seconds.
What MCP actually is (without the jargon)
MCP stands for Model Context Protocol. In one sentence: it's the wire that lets Claude talk to your real systems. Anthropic published the spec, the desktop app implements the client side, and anyone can publish a server. The server is just a small process that exposes "tools" (functions Claude can call) over a JSON-RPC interface on stdio.
The mental model is short:
- Claude desktop is the client. It launches servers as child processes.
- Each server is a command (
npx,node,python) plus arguments. - Servers advertise tools. Claude decides when to call them. You approve or auto-approve.
That's it. No SaaS in the middle. No webhook gateway. No plugin store. Your laptop runs the servers, Claude talks to them locally.
The config file: where it lives and why half the people get it wrong
The file is claude_desktop_config.json. Locations:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json - WSL Ubuntu (what I run): you edit the Windows path from inside Linux, because the Claude desktop app is a Windows process. From WSL that's
/mnt/c/Users/<you>/AppData/Roaming/Claude/claude_desktop_config.json.
The trap: the config has to be readable by the Claude desktop process, not your terminal. If you drop it in ~/.config/claude/ on WSL because that "feels right" on Linux, Claude opens, finds nothing, and you'll swear MCP is broken. It isn't. It's looking at the Windows path.
Structure is dead simple. One top-level key, mcpServers. Each entry gets a name, a command, args, and optionally env vars:
{
"mcpServers": {
"filesystem": {
"command": "node",
"args": [
"/usr/local/lib/node_modules/@modelcontextprotocol/server-filesystem/dist/index.js",
"/mnt/c/Users/lazar/projects/client-invoices"
]
},
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"POSTGRES_CONNECTION_STRING": "postgresql://app:***@localhost:5432/invoices"
}
}
}
}
Name, command, args, env. Every MCP server on the internet uses that shape. Once you've wired one, you've wired all of them.
The 6 servers I run every day
Here's the actual stack on my machine. Nothing theoretical — these run on live client projects right now.
- filesystem (official Anthropic): read/write/list inside a scoped directory. I point it at one project folder. Never
/. Never~. - postgres (official): connection string in, Claude writes SQL. Runs against the invoicing DB for a Serbian small business — 200 invoices/week that used to live in spreadsheets.
- gmail (community): inbox triage. Read last 50 unread, classify, draft replies, archive noise. Used to vet the source carefully — Gmail OAuth scopes are not something you copy-paste blindly.
- telegram (community): outbound only. Claude posts daily digests to a private channel so the founder gets one message at 8 AM instead of opening five dashboards.
- puppeteer (official): browser automation. Scheduled competitor pricing scrapes, checkout flow smoke tests, screenshotting forms. Replaces a small Cypress codebase.
- custom node server: ~150 lines of TypeScript wrapping a client's internal CRM REST API. Took about 40 minutes to write using Anthropic's SDK template. Claude now pulls lead data straight from their CRM with zero middleware.
Combined config, trimmed for readability:
{
"mcpServers": {
"filesystem": {
"command": "node",
"args": [
"/usr/local/lib/node_modules/@modelcontextprotocol/server-filesystem/dist/index.js",
"/mnt/c/Users/lazar/projects/client-invoices"
]
},
"postgres": {
"command": "node",
"args": ["/usr/local/lib/node_modules/@modelcontextprotocol/server-postgres/dist/index.js"],
"env": { "POSTGRES_CONNECTION_STRING": "postgresql://app:***@localhost:5432/invoices" }
},
"gmail": {
"command": "node",
"args": ["/home/lazar/mcp/gmail-server/dist/index.js"],
"env": { "GMAIL_CREDENTIALS_PATH": "/home/lazar/mcp/gmail-server/.creds.json" }
},
"telegram": {
"command": "node",
"args": ["/home/lazar/mcp/telegram-server/dist/index.js"],
"env": { "TELEGRAM_BOT_TOKEN": "***", "TELEGRAM_CHAT_ID": "-100***" }
},
"puppeteer": {
"command": "node",
"args": ["/usr/local/lib/node_modules/@modelcontextprotocol/server-puppeteer/dist/index.js"]
},
"crm": {
"command": "node",
"args": ["/home/lazar/mcp/crm-server/dist/index.js"],
"env": { "CRM_API_KEY": "***", "CRM_BASE_URL": "https://api.client-crm.com/v2" }
}
}
}
Six servers. One file. After restart, Claude sees ~30 tools available and decides which to call.
Writing your own server in about 40 minutes
The part most tutorials never reach. A custom MCP server is just a Node (or Python) process that uses Anthropic's SDK to declare tools and handle calls over stdio. Here's the minimum that actually works:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
const server = new Server(
{ name: "crm", version: "0.1.0" },
{ capabilities: { tools: {} } }
);
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "get_lead",
description: "Fetch a lead from the CRM by email",
inputSchema: {
type: "object",
properties: { email: { type: "string" } },
required: ["email"],
},
},
],
}));
server.setRequestHandler(CallToolRequestSchema, async (req) => {
if (req.params.name === "get_lead") {
const { email } = req.params.arguments as { email: string };
const res = await fetch(
`${process.env.CRM_BASE_URL}/leads?email=${encodeURIComponent(email)}`,
{ headers: { Authorization: `Bearer ${process.env.CRM_API_KEY}` } }
);
const data = await res.json();
return { content: [{ type: "text", text: JSON.stringify(data) }] };
}
throw new Error(`Unknown tool: ${req.params.name}`);
});
await server.connect(new StdioServerTransport());
Compile, point the config at the built JS, restart Claude. Done. The CRM server I wrote for a client was three tools (get_lead, update_lead_stage, list_leads_by_owner) and roughly that much code per tool.
The live demo: one prompt, three systems
The reason I built this stack. The prompt I actually type on a Monday morning:
"Pull last week's unpaid invoices from Postgres over €500, draft a polite follow-up email in the client's brand voice for each one, and post a summary to the Telegram channel."
What Claude does, in order:
- Calls
postgres.query→ returns 14 rows. - Calls
gmail.create_draft14 times, one per invoice, personalising amount and due date. - Calls
telegram.send_messageonce with the summary.
End to end: ~45 seconds. The founder opens Gmail, reviews 14 drafts already written, sends. Telegram digest already on their phone. Before this stack, that was three people, three tools, and a Monday morning meeting.
The Gmail triage workflow for the same client: founder was losing 90 minutes a day to inbox. Now Claude reads the last 50 unread, classifies, drafts the urgent ones, archives noise. Stopwatched at 12 minutes/day. That's not a marketing number — that's the actual measurement after two weeks.
Three mistakes that broke my setup the first month
Skip these. I already paid for them.
- Relative paths in
args. MCP servers are launched by the Claude desktop process, which has no clue where your project lives. Always absolute paths../server.jswill silently fail. npxfor daily servers.npxre-resolves and sometimes re-downloads the package on each Claude start. Slow, occasionally fails offline, and you'll get cryptic "server disconnected" errors. For anything I run daily, Inpm i -gthe package and pointcommandstraight atnodewith the absolute path to the installed script.- Filesystem server scoped too wide. The official filesystem server will happily let Claude overwrite files. Point it at
/or~and you've given an LLM write access to your SSH keys, your.envfiles, and your shell history. Scope to one project folder. If you need multiple, register multiplefilesysteminstances with different names.
A fourth one worth a sentence: when Claude says "server failed to start," check ~/Library/Logs/Claude/mcp*.log on Mac or %APPDATA%\Claude\logs\ on Windows. 95% of the time it's a path typo or a missing env var, and the log says exactly which.
Why bizflowai.io helps with this
This is the layer bizflowai.io already runs for clients: wiring Claude (or other LLMs) into the systems they actually use — Postgres, Gmail, Telegram, internal CRMs, invoicing tools like Fakturko — through MCP servers and custom connectors, so the AI does work instead of just talking about it. The config file, the scoped permissions, the custom servers for proprietary APIs, the daily-driver setup that doesn't fall over after a week — that's the build, and it's what most "Claude consultants" skip because it isn't a prompt template.
Frequently asked questions
What is MCP (Model Context Protocol)?
MCP stands for Model Context Protocol. It is the wire that lets Claude talk to real systems like your filesystem, database, Gmail, or Telegram. Without MCP, Claude is a chatbot trapped in a browser tab. With it, Claude becomes an operator that can read, write, and act on your business systems through a standardized JSON-RPC interface.
Where do I put the claude_desktop_config.json file?
On Mac, the file lives in Library/Application Support/Claude. On Windows, it's in AppData/Roaming/Claude. On WSL Ubuntu, you must edit the Windows path from inside Linux because the Claude desktop app runs as a Windows process. The config must be readable by the Claude desktop process, not your terminal, or Claude will open and see no servers.
How do I structure an MCP server entry in the config?
The config is JSON with one top-level key called mcpServers. Inside, each server gets a name, a command (usually npx, node, or python), and an args list pointing to the server script or npm package. If the server needs credentials, add an env block underneath. Every MCP server on the internet follows this same shape: name, command, args, env.
What are the most useful MCP servers to install?
Six servers cover most real work: filesystem (read/write files in a scoped directory), Postgres (natural-language database queries), Gmail (inbox triage and reply drafting), Telegram (outbound summaries to a channel), Puppeteer (browser automation and scraping), and a custom Node server wrapping your own internal APIs. Together they turn Claude from a chatbot into an operator across files, data, email, messaging, and the web.
Why should I scope the filesystem MCP server to one folder?
Scoping the filesystem server to a single project folder prevents Claude from accessing sensitive files like SSH keys. A common mistake is pointing the filesystem server at the root directory or home drive, which exposes everything on the machine. Best practice is one tight folder per project, so Claude can only read, write, and list files inside that specific directory.
Want more like this?
I publish practical AI automation, GenAI engineering, and faceless content workflows on YouTube every week.
Subscribe to bizflowai.io on YouTube — never miss a new tutorial.
Planning an AI automation project or need a second opinion on your architecture?
Connect with me on LinkedIn — Lazar Milicevic, GenAI Engineer & bizflowai.io Founder.
Visit bizflowai.io for our services, case studies, and AI consulting.
Frequently asked questions
What is MCP (Model Context Protocol)?
MCP stands for Model Context Protocol. It is the wire that lets Claude talk to real systems like your filesystem, database, Gmail, or Telegram. Without MCP, Claude is a chatbot trapped in a browser tab. With it, Claude becomes an operator that can read, write, and act on your business systems through a standardized JSON-RPC interface.
Where do I put the claude_desktop_config.json file?
On Mac, the file lives in Library/Application Support/Claude. On Windows, it's in AppData/Roaming/Claude. On WSL Ubuntu, you must edit the Windows path from inside Linux because the Claude desktop app runs as a Windows process. The config must be readable by the Claude desktop process, not your terminal, or Claude will open and see no servers.
How do I structure an MCP server entry in the config?
The config is JSON with one top-level key called mcpServers. Inside, each server gets a name, a command (usually npx, node, or python), and an args list pointing to the server script or npm package. If the server needs credentials, add an env block underneath. Every MCP server on the internet follows this same shape: name, command, args, env.
What are the most useful MCP servers to install?
Six servers cover most real work: filesystem (read/write files in a scoped directory), Postgres (natural-language database queries), Gmail (inbox triage and reply drafting), Telegram (outbound summaries to a channel), Puppeteer (browser automation and scraping), and a custom Node server wrapping your own internal APIs. Together they turn Claude from a chatbot into an operator across files, data, email, messaging, and the web.
Why should I scope the filesystem MCP server to one folder?
Scoping the filesystem server to a single project folder prevents Claude from accessing sensitive files like SSH keys. A common mistake is pointing the filesystem server at the root directory or home drive, which exposes everything on the machine. Best practice is one tight folder per project, so Claude can only read, write, and list files inside that specific directory.