mcp server
The actual working config for ~/.cursor/mcp.json — two transports, env-var secrets, the 5-minute setup flow, and the debugging loop for the red dot.
TL;DR — Cursor reads MCP servers from
~/.cursor/mcp.json. Two transport modes (stdio, HTTP). Secrets go in env, referenced via${env:NAME}. Red dot = missing env var 90% of the time. This post is my actual working config plus the five-minute setup that catches every mistake I've made over the last year.
I run 8 MCP servers in Cursor every day. This is the setup that actually works — no theory, no marketing, just the config file and the debugging loop. For the broader pattern of when to build MCP, read MCP Server vs REST API. For production-grade architecture, read MCP Server Architecture.
~/.cursor/mcp.jsonEverything lives here. Create the file if it doesn't exist:
mkdir -p ~/.cursor
touch ~/.cursor/mcp.json
A minimal working config with one HTTP and one stdio server:
{
"mcpServers": {
"Context7": {
"url": "https://mcp.context7.com/mcp",
"headers": { "CONTEXT7_API_KEY": "${env:CONTEXT7_API_KEY}" }
},
"Fly": {
"command": "npx",
"args": ["-y", "@flydotio/mcp"]
}
}
}
Two transports:
url + headers) — hosted multi-tenant servers. Cursor POSTs JSON-RPC. Bearer tokens go in headers.command + args) — local child process. Cursor spawns it and talks over stdin/stdout. Good for tools that need your filesystem or dev env.Env var interpolation: ${env:VAR_NAME} reads from your shell env. ${input:thing} prompts you in Cursor on first use. Never inline a raw token. I've seen MCP configs committed to GitHub with live API keys — don't be that person.
{
"mcpServers": {
"Supabase": { "url": "https://mcp.supabase.com/mcp?project_ref=<ref>&features=database%2Cdocs" },
"GitHub": { "url": "https://api.githubcopilot.com/mcp/",
"headers": { "Authorization": "Bearer ${input:github_mcp_pat}" } },
"Context7": { "url": "https://mcp.context7.com/mcp",
"headers": { "CONTEXT7_API_KEY": "${env:CONTEXT7_API_KEY}" } },
"AppHandoff": { "url": "https://apphandoff.com/api/mcp",
"headers": { "Authorization": "Bearer ${env:APPHANDOFF_TOKEN}" } },
"Fly": { "command": "npx", "args": ["-y", "@flydotio/mcp"] },
"Playwright": { "command": "npx", "args": ["-y", "@playwright/mcp@latest"] },
"shadcn": { "command": "npx", "args": ["shadcn@latest", "mcp"] },
"Infisical": { "command": "npx", "args": ["-y", "@infisical/mcp"],
"env": { "INFISICAL_TOKEN": "${env:INFISICAL_TOKEN}" } }
}
}
Full breakdown of what each one does in 12 Real MCP Server Examples.
~/.cursor/mcp.json. Add one server to start — Context7 is the safest first pick (free tier, no consequences)..zshrc / .bashrc:
export CONTEXT7_API_KEY="c7sk-..."
Cmd-Q), not just reload the window. MCP discovery runs on start.tools/list succeeded. Red = something's wrong.If step 4 is red, go to the debugging section below.
| Situation | Transport |
|---|---|
| Tool needs to read/write your filesystem | stdio |
| Tool runs a subprocess on your machine (Playwright, npm, shell) | stdio |
| Hosted SaaS with multi-tenant auth (Supabase, GitHub, Context7) | HTTP |
| You want the same config on your teammate's machine | HTTP |
| Prototyping your own MCP | stdio (switch to HTTP before shipping) |
stdio is easier to debug (you can run the command by hand in a terminal). HTTP scales better (multiple agents, multiple machines). My rule: stdio for local-only tools, HTTP for anything hosted.
Cursor already has rules (.cursor/rules/*.mdc) and inline context. Why add MCP?
Rules = "here's how we write code." MCP = "go look at what the current production database actually contains right now." You want both.
If you're still relying on copy/paste of schema + docs into chat, you're doing the job MCP was built to eliminate.
The red dot means tools/list failed. In order of how often it's the cause:
.zshrc unless you launched Cursor from the terminal (open -a Cursor) or set it via launchctl setenv.command + args from your terminal — if it errors, your config will error identically.curl -s -X POST "$URL" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
If curl returns tools, Cursor should too. If it doesn't, the URL or token is wrong.npx -y with a wrong package name hangs. Pin the exact name + version.Cursor's MCP panel has a "view logs" link next to each server. Read those first. The logs tell you exactly which JSON-RPC call failed.
Three escalating levels of safety:
export CONTEXT7_API_KEY=... in your shell profile. Fine for solo work.mcp.json works across machines without the secrets leaking.${input:name} in mcp.json so Cursor prompts you on first use and stores the value in its keychain. Good for rotating tokens.One tripwire: macOS GUI apps don't inherit .zshrc exports by default. Either launch Cursor from terminal (open -a Cursor), or set the env globally via launchctl setenv. This is the #1 "why doesn't my MCP work" I see from new users.
1. "What did we ship yesterday?" — Cursor asks GitHub MCP for yesterday's merged PRs, summarizes, drafts a changelog entry. 10 seconds.
2. "Why is checkout broken on prod?" — Cursor asks Fly MCP for the last 60s of logs, Supabase MCP for the last 10 orders rows, Context7 for Stripe webhook spec. Root-cause hypothesis in under a minute.
3. "Scaffold a new admin page using our existing patterns." — Cursor asks shadcn MCP for the right components, filesystem MCP for an existing page as reference, writes the new page, runs the linter. Done in one prompt.
None of these would be MCP-worthy if you only wanted them once. They're MCP-worthy because you do them 40 times a week.
Here's a verbatim sequence from a Cursor session on this repo's backend last week — "why did our contact form fail at 3:14 EST?":
Fly.logs (stdio) with app=inspired-api, since=3h — pulls ~60KB of log output.mailgun proxy timeout.Supabase MCP (HTTP) → SELECT * FROM contact_submissions ORDER BY created_at DESC LIMIT 5 — confirms the row never got written (submission never reached the DB).GitHub MCP → search_code repo:... mailgun proxy timeout — finds the retry config and the Fly secret expected.Infisical MCP → lists secrets in prod, notices MAILGUN_API_KEY was rotated but not redeployed.Five tool calls, ~90 seconds, no context-switch. None of it touched the web UI of Fly, Supabase, GitHub, or Infisical. That's the whole pitch for MCP in Cursor — your agent becomes the debugging console.
Cursor added streamable HTTP transport + SSE support over the last year — remote MCPs are now first-class. The upshot: you can deploy an MCP server to Fly or Cloudflare Workers, hand teammates the URL + token, and they get the same tools you do without installing anything.
I deploy most client MCPs this way through AppHandoff. See MCP Server Architecture for what production-grade remote MCP deployment looks like (rate limits, circuit breakers, response caps, structured errors).
~/.cursor/mcp.json existsIf all boxes are ticked and it still doesn't work — something's wrong with the specific server, not your config. Swap it for a different one and re-verify.
If your team has an internal system that should speak MCP, or you've hit a wall debugging one you built, describe it here. I ship production MCP servers and maintain several of the ones in this post.
// keep reading
mcp server · 8 min
Four layers every production MCP server needs — transport, JSON-RPC shape, tool definitions, and guardrails. With real code for rate limiting, circuit breakers, and structured errors.
mcp server · 8 min
MCP is not a REST replacement. Decision table, real code from both, side-by-side comparison across 10 dimensions, and the hybrid pattern that ships in production.