Set up MCP servers in Cursor
PLCursor connects its agent to MCP (Model Context Protocol) servers listed in a JSON file: .cursor/mcp.json for one project or ~/.cursor/mcp.json for every project. Each entry is either a local command that Cursor starts or a remote URL. A server is ready only when the agent’s answers cite a real tool call made with least-privilege credentials.
This page is for the developer wiring Cursor to the systems they touch every day, and for the tech lead who wants one shared, reviewable MCP file for the whole team. You ask the agent whether users has an email_verified column. It finds a migration from six months ago and answers with confidence, and the answer is wrong because a later hotfix dropped the column. With a database server connected, the agent reads the live schema instead. Adding the server takes five minutes. Making sure it can only read, and that you can tell when it actually did, is the rest of this page.
What you get from MCP setup in Cursor
Section titled “What you get from MCP setup in Cursor”- A committed
.cursor/mcp.jsonwith GitHub, Postgres and Playwright servers and no secret in it. - Read-only credentials for every server that does not need to write.
- A smoke-test prompt that proves each server answers, and an evidence prompt that shows you which answers came from a tool.
- A symptom-to-fix table for the servers that fail to start, fail to authenticate or never get called.
Where does Cursor read MCP configuration?
Section titled “Where does Cursor read MCP configuration?”Cursor merges two files. Both use the same shape: a top-level mcpServers object with one entry per server.
| Scope | File | Use it for |
|---|---|---|
| Project | .cursor/mcp.json in the repository root | Servers the whole team needs for this codebase. Commit it. |
| Global | ~/.cursor/mcp.json | Personal servers you want in every project. |
Each entry is either a local (stdio) server or a remote server; either kind can add an auth block. The keys below come from the mcp.json schema inside Cursor’s own @cursor/sdk package (version 1.0.32, checked 2026-09-26), because cursor.com was unreachable from our verification environment that day. We did not re-check this schema on cursor.com on 2026-10-12; @cursor/sdk is at 1.0.37 as of 2026-10-07.
| Kind | Keys | Example |
|---|---|---|
| Local (stdio) server that Cursor starts | command, args, env, cwd, envFile | npx @playwright/mcp@latest |
| Remote server over HTTP | url, headers | https://api.githubcopilot.com/mcp/readonly |
| Optional on either kind, when the server needs a pre-registered OAuth client | auth with CLIENT_ID, optional CLIENT_SECRET, optional scopes | a company-internal server |
Write secrets as ${env:NAME} references. Cursor substitutes the value of the environment variable NAME when it loads the file, so the file itself holds no token and can be committed.
Connect your first server: GitHub, read-only
Section titled “Connect your first server: GitHub, read-only”GitHub’s hosted server needs no local runtime, and it has a read-only endpoint. That makes it the safest first server: the agent can read issues, pull requests and CI logs but cannot push, merge or comment.
-
Create a fine-grained personal access token. In GitHub, go to Settings → Developer settings → Personal access tokens → Fine-grained tokens. Limit it to the repositories you work on and grant read-only access to contents, issues, pull requests and Actions.
-
Put the token in your environment, not in a file in the repository. First save it to a file outside the repository that only you can read:
Terminal window mkdir -p ~/.config/tokens(umask 077; cat > ~/.config/tokens/github-mcp) # paste the token, press Enter, then Ctrl-DThen add this line to your shell profile (
~/.zshrcor~/.bashrc) and open a new terminal:Terminal window export GITHUB_PAT="$(cat ~/.config/tokens/github-mcp)" -
Create
.cursor/mcp.jsonin the repository root:{"mcpServers": {"github": {"url": "https://api.githubcopilot.com/mcp/readonly","headers": {"Authorization": "Bearer ${env:GITHUB_PAT}"}}}}The
/readonlysuffix is GitHub’s own read-only endpoint, and GitHub offers the same suffix per toolset, such ashttps://api.githubcopilot.com/mcp/x/actions/readonly. Drop the suffix only when you want the agent to open pull requests or comment. -
Quit Cursor completely and start it again. Cursor reads the environment and
mcp.jsonwhen it starts. A new chat window is not enough. -
Check the server’s status. Open Cursor Settings and find the MCP section (GitHub’s install guide calls it Tools & Integrations → MCP Tools; other guides call it MCP). A green dot next to
githubmeans Cursor connected and loaded its tools. A red dot or an error means the server did not start. The table in When MCP setup in Cursor breaks lists the fixes. -
Run the smoke test below in a new Agent chat. Expect to see tool-call entries in the chat, not only prose.
If a server appears in the list but the call fails, the server started and the credentials are wrong. If it does not appear at all, the server never started.
Add a database and a browser
Section titled “Add a database and a browser”Most teams get the next biggest gain from two more servers: a database the agent can inspect and a browser it can drive.
Postgres, with a read-only role
Section titled “Postgres, with a read-only role”Postgres MCP Pro (crystaldba/postgres-mcp) is a Python package on PyPI, so it runs with uvx, not npx. Its --access-mode=restricted flag runs queries in read-only transactions with execution-time limits. Its last PyPI release is 0.3.0 (May 2025); if you need an actively maintained option, see DBHub on the database MCP servers page.
{ "mcpServers": { "postgres": { "command": "uvx", "args": ["postgres-mcp", "--access-mode=restricted"], "env": { "DATABASE_URI": "${env:DATABASE_URI}" } } }}The server reads DATABASE_URI, not DATABASE_URL. With only DATABASE_URL set, the server exits at startup with “No database URL provided” and shows a red dot. For stdio servers, you can also set "envFile": ".env.mcp" and keep the variables in a gitignored file, which avoids the problem of Cursor not seeing variables from your shell profile.
Playwright, for checking the UI the agent just changed
Section titled “Playwright, for checking the UI the agent just changed”{ "mcpServers": { "playwright": { "command": "npx", "args": ["-y", "@playwright/mcp@latest", "--isolated"] } }}--isolated keeps the browser profile in memory. Playwright’s README warns that a persistent profile can be used by only one browser at a time, so two agents or two Cursor windows sharing the default profile conflict. Playwright’s own README also suggests that coding agents may do better with its CLI plus skills than with MCP, because MCP tool schemas and page snapshots take context. Start with MCP. Switch if your chats hit context limits. The Playwright MCP guide covers both.
The combined team file
Section titled “The combined team file”Merge the three entries into one .cursor/mcp.json and commit it:
{ "mcpServers": { "github": { "url": "https://api.githubcopilot.com/mcp/readonly", "headers": { "Authorization": "Bearer ${env:GITHUB_PAT}" } }, "postgres": { "command": "uvx", "args": ["postgres-mcp", "--access-mode=restricted"], "env": { "DATABASE_URI": "${env:DATABASE_URI}" } }, "playwright": { "command": "npx", "args": ["-y", "@playwright/mcp@latest", "--isolated"] } }}Each teammate then needs only the two environment variables, uv for uvx, and Node.js for npx. Add those three prerequisites to the README next to the file.
Remote servers that sign you in with OAuth
Section titled “Remote servers that sign you in with OAuth”Many hosted servers, such as Linear (https://mcp.linear.app/mcp), need only a URL. Cursor runs the OAuth sign-in in your browser when the server first connects, so there is no token to store:
{ "mcpServers": { "linear": { "url": "https://mcp.linear.app/mcp" } }}Add an auth block only when a server’s documentation gives you a pre-registered client ID. Reference the client secret with ${env:…} like any other secret.
How do you know the agent used the tool instead of guessing?
Section titled “How do you know the agent used the tool instead of guessing?”A connected server does not guarantee that the agent called it. The agent decides per prompt, and a confident answer built from a stale migration file looks the same as one built from the live schema. Check the evidence, not the prose:
- Look for the tool calls in the transcript. Cursor shows each MCP tool call in the chat, and you can open it to see what was sent and returned. An answer about the database with no
postgrestool call above it came from your code, not from the database. - Ask for the raw result. The evidence prompt below makes the agent quote what the tool returned, so a mismatch between the quote and the conclusion is visible at a glance.
- Cross-check one fact with a deterministic command. For schema questions,
psql "$DATABASE_URI" -c '\d users'in a terminal settles it in one line. Do this the first few times you rely on a server, not on every prompt. - Let tests carry the rest. When MCP data feeds a code change, such as a migration, the gate is the same as for any agent change: the migration applies to a fresh local database and the test suite passes. You review the test results and the migration’s intent, not every generated line.
- Let credentials fail loudly. With read-only tokens and roles, an accidental write returns an error instead of changing data. An error in the chat is evidence that the guardrail works.
The tech lead signs off on which servers the team file contains and what each credential can do. Each developer signs off on their own change through the tests, as with any other agent work. Nobody needs to read MCP traffic line by line.
Keep MCP servers safe for a team
Section titled “Keep MCP servers safe for a team”- Commit the file, never the token. A committed
.cursor/mcp.jsonwith only${env:…}references is reviewable in pull requests. A token pasted into the file is a leaked token, even in a private repository. - Read a cloned repository’s
.cursor/mcp.jsonbefore you approve its servers. A stdio entry is a command that runs on your machine with your environment variables. Treat it like a script from the same source. - Keep approval on for servers that write. How Cursor approves tool calls, including MCP calls, is set by the agent’s run modes (Auto-review, Allowlist or Run Everything; Cursor’s Agent security documentation). Let only read-only servers run without approval, and review the approval setting whenever you add a server that can write.
- Prefer the vendor’s official server. GitHub, Playwright, Linear and Sentry maintain their own servers. Community wrappers of the same API add a maintainer you have not reviewed.
- Load only what you use. Every connected server adds its tool definitions to the agent’s context before you type. Turn off servers and tools you do not need for the current project in the MCP settings list, and see reducing MCP token cost for the larger fixes.
For scanning configurations, prompt-injection containment and a team approval policy, see securing MCP servers.
When MCP setup in Cursor breaks
Section titled “When MCP setup in Cursor breaks”| Symptom | Likely cause | Recovery |
|---|---|---|
| Red dot or no entry for a stdio server | The command is missing from the PATH Cursor sees (npx, uvx, docker), or the JSON is invalid | Run the exact command and args in a terminal. Install the missing runtime. Validate the file with python3 -m json.tool .cursor/mcp.json. Restart Cursor fully. |
401 or “Bad credentials” from GitHub | ${env:GITHUB_PAT} was empty when Cursor started, or the token lacks access to the repository | Confirm echo $GITHUB_PAT prints a value in a new terminal. If Cursor was started from the Dock or Start menu, it may not inherit your shell profile. Quit and relaunch it from an environment that has the variable, or use envFile for stdio servers. |
Red dot on postgres; log says “No database URL provided” | DATABASE_URI is unset or empty (often only DATABASE_URL is set) | Set DATABASE_URI and restart Cursor fully. |
| Postgres server starts, every query fails | The role cannot connect (wrong host, password or grants) | Test the same string with psql "$DATABASE_URI" -c 'select 1'. |
| Tools are listed but the agent answers without calling them | The prompt did not make the tool relevant | Name the server and the tool in the prompt (“use the postgres MCP server to…”), and ask for the raw output as in the evidence prompt. |
| Playwright fails with a profile or browser-in-use error | Two sessions share one persistent browser profile | Add --isolated, or give each window its own --user-data-dir. |
| A server worked yesterday and is gone today | @latest pulled a breaking release, or the remote endpoint changed | Pin a version, for example @playwright/mcp@0.0.83, and bump it deliberately. |
| Answers get worse as you add servers | Too many tool definitions compete for context | Disable unused servers and tools per project. Keep the team file to the servers the codebase needs. |
For errors specific to other agents, and for the full list of connection failures, see MCP server connection issues.
Using the same servers in Claude Code and Codex
Section titled “Using the same servers in Claude Code and Codex”The servers are the same, but the configuration is not portable file-for-file. Claude Code reads a project .mcp.json and expands ${VAR}. Codex writes [mcp_servers.<name>] tables into ~/.codex/config.toml. If your team runs more than one agent, keep one list of servers and credentials and configure each tool from it. See MCP setup in Claude Code and MCP setup in Codex.
Where to go next with MCP in Cursor
Section titled “Where to go next with MCP in Cursor”This page follows the PRD workflow in the Cursor quick start. The next step teaches you to choose the context the agent sees in each prompt.
Did this guide help you?
One tap, no sign-in.
What will you do with it?
How did it read?
What got in the way? Pick any that apply
Your role?
Thanks. I read every answer. Michał Jaskólski
Did this guide help?
One tap each. Close whenever you like.
no sign-in