Productivity
    cursorcodingautomation

    How to configure Model Context Protocol servers in Cursor

    An agent that can run SELECT * FROM orders can usually also run DELETE FROM orders unless you stop it. Here's the exact MCP config, and where to draw the read-only line.

    Editor: Paul RadfordJul 19, 20268 min read
    How to configure Model Context Protocol servers in Cursor

    How to configure Model Context Protocol servers in Cursor

    Model Context Protocol (MCP) is a standard that lets Cursor's agent talk to things outside your codebase: a local database, an internal REST API, a ticketing system. You configure it by dropping a small JSON file that tells Cursor which server process to launch and what credentials to pass it. Once connected, the agent can call those tools mid-conversation the same way it calls its built-in file-editing tools, which means it can query live data instead of guessing from stale training knowledge.

    That last part is the whole point and also the whole risk. An agent that can run SELECT * FROM orders can usually also run DELETE FROM orders unless you stop it. The rest of this piece walks through the exact config, how discovery works during a chat, and where to draw the read-only line.

    What MCP actually adds to Cursor

    Cursor's chat and agent modes already read your open files and repo. MCP extends that by letting the agent start a separate process (the "MCP server") that exposes a set of named tools, resources, or prompts over a lightweight JSON-RPC-based protocol. Cursor acts as the MCP client. It launches the server, asks it what capabilities it has, and then makes those capabilities available to the model as callable tools during the session.

    This is deliberately generic. The same protocol works whether the server wraps a SQLite file, a Postgres instance, a REST API, or something like Figma or Slack. Anthropic originally proposed MCP as an open standard, and the ecosystem has grown a large set of reference and community servers, catalogued in the modelcontextprotocol/servers repository on GitHub. Cursor's own documentation covers the client-side setup in detail in its Model Context Protocol guide, which is the source I'd point any teammate to first.

    Where does the config file actually go?

    Cursor reads MCP server definitions from a file named mcp.json. You can scope it two ways:

    • Project-level: .cursor/mcp.json inside the repo, checked in or not depending on whether it contains secrets.
    • Global: a user-level config that applies across all projects, useful for tools like a personal Notion or GitHub connector you use everywhere.

    Both use the same schema: a top-level mcpServers object where each key is a server name you choose, and the value describes how to launch it.

    Connecting a local SQLite database

    For local data, most people run a small SQLite MCP server via uvx (the Python uv tool runner) or npx, depending on which implementation they picked from the servers repo. A typical project-level config looks like this:

    json
    1{
    2 "mcpServers": {
    3 "app-sqlite": {
    4 "command": "uvx",
    5 "args": [
    6 "mcp-server-sqlite",
    7 "--db-path",
    8 "/Users/you/projects/app/data/app.db"
    9 ]
    10 }
    11 }
    12}

    Save that as .cursor/mcp.json, then open Cursor's settings panel under MCP and you should see app-sqlite listed with a green status once the server process starts successfully. If it shows red or "no tools found," it's almost always one of three things: the binary isn't on PATH, the db-path is wrong, or the server crashed on startup because the file doesn't exist yet. Check the server logs in the MCP settings panel before assuming the config is broken.

    Connecting a REST API

    For an external API, the pattern is similar but you typically pass a base URL and an auth token through environment variables rather than command-line args, so the token doesn't end up in shell history or process listings visible to other users on the machine.

    json
    1{
    2 "mcpServers": {
    3 "internal-api": {
    4 "command": "node",
    5 "args": ["./mcp-servers/rest-bridge.js"],
    6 "env": {
    7 "API_BASE_URL": "https://api.example.com/v1",
    8 "API_KEY": "your-token-here"
    9 }
    10 }
    11 }
    12}

    Here rest-bridge.js is a small server you write (or adapt from a reference implementation) that translates MCP tool calls like getOrder(id) into HTTP requests against your API and returns the JSON response as a tool result. Writing that bridge is more work than the SQLite case because there's no universal "REST API server," you're mapping specific endpoints to specific tool definitions. If your API already has an OpenAPI spec, several community projects will generate a basic MCP wrapper from it, but expect to hand-tune the tool descriptions so the model understands what each endpoint actually does and when to call it.

    One real gotcha here: if API_KEY is committed inside .cursor/mcp.json in a shared repo, it's in your git history the moment someone runs git add -A. Keep secrets in a separate untracked file or reference an environment variable that's already set in your shell, and gitignore the config file if it contains anything sensitive.

    How does the agent know when to use these tools?

    During a chat, Cursor sends the model a list of available tools, both its own built-ins (read file, edit file, run terminal command) and whatever your MCP servers exposed, along with their names, parameter schemas, and descriptions. The model decides on its own, turn by turn, whether a tool call is useful for answering your prompt.

    This means the quality of the tool's description matters enormously. If your SQLite server just exposes a generic query(sql) tool with no description, the model has to infer from context that it should use it, and it sometimes won't bother, answering from guesswork instead. If you ask "what's the current order count for customer 42," a well-integrated agent will call the tool, get real rows back, and answer from them. A poorly wired one will hallucinate a plausible-sounding number. I've seen this happen more than once when a server's tool list wasn't refreshed after adding new tables, so always check the MCP panel's tool list matches what you expect before trusting an answer that depends on it.

    Latency is worth knowing about too. Each tool call is a round trip: model decides to call, Cursor invokes the local process, the process runs the query or the HTTP request, result comes back, model incorporates it and continues. For a local SQLite lookup this adds maybe a few hundred milliseconds. For a REST API with a slow backend, or a chain of several tool calls in one agent turn, you can add multiple seconds to a response. It's rarely the bottleneck compared to model generation time, but it's not free.

    The actual security trade-off: read/write vs read-only

    This is the part people skip and then regret. An MCP server you configure has whatever permissions its underlying connection has. If your SQLite server points at a database file with write permissions on disk, and its query tool doesn't restrict SQL to SELECT statements, the agent can run DROP TABLE if it decides that's the right move to fulfill your request. Agents don't have malicious intent, but they follow instructions literally, and a prompt like "clean up test data" is exactly the kind of instruction that can go badly wrong against a live table.

    Concrete rules I actually follow:

    • Point local database MCP servers at a copy of your data, not the production file, whenever you're exploring or debugging. Copying a SQLite file is a single cp command; there's no excuse not to.
    • For servers that support a read-only flag or a restricted connection string, use it by default. Many reference SQLite and Postgres MCP servers accept a --readonly argument or can be pointed at a database role with SELECT-only grants. Use the database's own permission system as the real boundary, not just the server's promise to behave.
    • For REST APIs, scope the API key to the narrowest role available. If your internal API supports separate read and write tokens, give Cursor the read token unless you have a specific, ongoing need for the agent to create or modify records through chat.
    • Treat any write-capable MCP connection the way you'd treat giving a new junior engineer direct production database access on day one: fine in specific supervised situations, not fine as a permanent default.
    • Review Cursor's tool-call approval settings. Depending on how you've configured agent mode, some tool calls require a manual approval click before they run; for anything with write access, keep that approval step on rather than switching to full auto-run.

    The honest trade-off is speed versus safety. Read-only access covers the majority of genuinely useful cases: "why did this query return zero rows," "show me the schema for the orders table," "check what the API returns for this customer ID." Write access unlocks things like agent-driven data migrations or seeding test fixtures, which are real time-savers, but only when you've already decided the blast radius is acceptable if the model does something you didn't intend.

    A rollout sequence that has worked for me

    Start read-only on a copy of production data, watch how the agent actually uses the tools for a week of normal work, then decide if write access earns its keep for that specific server. Don't grant write access to more than one server at a time, so if something goes wrong you know exactly where to look. Keep credentials out of committed config files from day one, because retrofitting that later means rewriting git history. And check the tool list in Cursor's MCP settings panel after every server update, since a silent schema change is the most common way an agent starts calling a tool that no longer does what you think it does.

    Advertisement

    728 × 90 — Leaderboard — Google AdSense

    Related Articles