Cursor Config (.cursor/mcp.json)
{
"mcpServers": {
"my-dev-tools": {
"command": "node",
"args": ["./mcp-server/dist/index.js"],
"env": {
"DATABASE_URL": "${env:DATABASE_URL}"
}
},
"my-python-tools": {
"command": "uv",
"args": ["run", "mcp-server/main.py"]
}
}
}
| Field | Notes |
|---|
command | Executable the client spawns (node, python, uv, npx) |
args | Path to built entry file |
env | Use ${env:VAR} — never commit secrets |
| Settings UI | Cursor → Settings → MCP (same shape) |
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({ name: "my-dev-tools", version: "1.0.0" });
server.tool(
"lookup_user",
{
email: z.string().email().describe("User email to look up"),
limit: z.number().int().min(1).max(10).default(1),
},
async ({ email, limit }) => {
const users = await db.users.findByEmail(email, { limit });
return {
content: [{ type: "text", text: JSON.stringify(users, null, 2) }],
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("my-dev-tools")
@mcp.tool()
def lookup_user(email: str, limit: int = 1) -> str:
"""Look up a user by email. Read-only staging DB."""
users = db.find_users(email=email, limit=min(limit, 10))
return json.dumps(users)
if __name__ == "__main__":
mcp.run()
| Do | Don't |
|---|
One action per tool (lookup_user) | One tool that does everything |
| Zod / type hints on every param | Raw user strings → SQL |
| Return small JSON text | Return megabytes |
| Read-only for exploration | Prod writes without guardrails |
Clear description on each param | Cryptic param names |
Test Commands
cd mcp-server && npm run build
npx @modelcontextprotocol/inspector node ./mcp-server/dist/index.js
Troubleshooting
| Symptom | Fix |
|---|
| Server not listed | Check JSON syntax; restart Cursor |
| Red / disconnected | Run server manually; check args path and node version |
| Tool never called | Name tool clearly; describe when to use it in the description |
| Empty response | Log inside handler; validate env vars are set |
| Timeout | Return less data; add pagination param |
Pre-Prod Security
- [ ] Read-only DB credentials for dev tools
- [ ] Inputs validated with schema
- [ ] No secrets in tool responses
- [ ] Audit log of tool invocations
- [ ]
.cursorignore excludes .env*