Blog ·

mcp.json example: safer configs for Claude Code, Cursor and VS Code

A risky mcp.json example next to a safer one for each tool, with every key checked against the vendor docs: pinned versions, scoped paths, no secrets in the file.

An mcp.json example has one object that maps server names to how each server starts: command, args and env for a local server, or a url and headers for a remote one. Claude Code (.mcp.json) and Cursor (.cursor/mcp.json) put that object under mcpServers; VS Code (.vscode/mcp.json) puts it under servers. The examples below show a risky file first, then a safer version for each tool: exact versions, folder access limited to what the job needs, HTTPS for remote servers, and secrets referenced from the environment instead of typed in. Every key name comes from the vendor's documentation, listed under Sources.

A risky mcp.json example, and what is wrong with it

This file loads, works, and is the kind of thing that turns up on developer machines every day.

{
  "mcpServers": {
    "files": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/"]
    },
    "github": {
      "command": "npx",
      "args": ["-y", "some-github-mcp"],
      "env": { "GITHUB_TOKEN": "ghp_paste-your-real-token-here" }
    },
    "notes": {
      "type": "http",
      "url": "http://notes.example.net/mcp"
    }
  }
}

It has four problems. The filesystem server is given /, the whole disk; the MCP documentation warns that this server runs with your user account permissions and can do any file operation you can. Neither package is pinned, so npx -y fetches whatever was published last each time it starts, which is how one bad release of a package reaches every machine at once. The GitHub token is typed into the file, where it gets copied, synced and committed. And the remote server uses plain http on a host nobody on the team owns.

None of these is exotic. They map to the supply chain, secret exposure and over-broad access risks in the MCP specification's security best practices, and to several of the 12 checks in our MCP security checklist.

A safer .mcp.json example for Claude Code

Claude Code reads project servers from .mcp.json at the repository root. Entries with a command are local stdio servers; remote servers set type to "http". Claude Code expands ${VAR} and ${VAR:-default} in command, args, env, url and headers.

{
  "mcpServers": {
    "files": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem@1.2.3",
        "/Users/me/projects/my-app/docs"
      ]
    },
    "github": {
      "type": "http",
      "url": "https://api.example.com/mcp",
      "headers": { "Authorization": "Bearer ${GITHUB_MCP_TOKEN}" }
    }
  }
}

The filesystem server now gets one project folder, given as an absolute path, which the MCP documentation recommends for this server. The package carries an exact version; 1.2.3 stands for whichever version your team reviewed. The token comes from the developer's environment. Two details from the docs are worth knowing. If a variable is unset and has no default, the server still loads with the literal text, and claude mcp list shows a warning. And Claude Code reads some of its own credential variables, such as ANTHROPIC_API_KEY, as empty in a remote server's URL and headers, which keeps them from being sent to that server.

A safer mcp.json example for Cursor

Cursor reads .cursor/mcp.json in a project and ~/.cursor/mcp.json for every project. It uses the same mcpServers key, but a different interpolation syntax: ${env:NAME} for environment variables and ${workspaceFolder} for the project root.

{
  "mcpServers": {
    "local-tools": {
      "command": "python",
      "args": ["${workspaceFolder}/tools/mcp_server.py"],
      "env": { "API_KEY": "${env:API_KEY}" }
    },
    "remote-server": {
      "url": "https://api.example.com/mcp",
      "headers": { "Authorization": "Bearer ${env:MY_SERVICE_TOKEN}" }
    }
  }
}

Cursor also supports envFile for local servers, pointing at a file such as ${workspaceFolder}/.env. Its docs note that envFile only works for stdio servers, so remote servers should take their tokens from the shell environment through interpolation. If you use an env file, keep it out of git.

A safer mcp.json example for VS Code and GitHub Copilot

VS Code's .vscode/mcp.json has a top-level servers object, an optional inputs array, and requires a type on every server: "stdio", "http" or "sse". Inputs let VS Code ask for a value when the server starts, and "password": true hides what you type.

{
  "inputs": [
    {
      "type": "promptString",
      "id": "api-token",
      "description": "API token",
      "password": true
    }
  ],
  "servers": {
    "docs": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "some-docs-mcp@1.2.3"],
      "env": { "DOCS_API_KEY": "${input:api-token}" }
    },
    "remote": {
      "type": "http",
      "url": "https://api.example.com/mcp",
      "headers": { "Authorization": "Bearer ${input:api-token}" }
    }
  }
}

Nobody has to type the token into a file, and the file is safe to commit. VS Code also supports envFile and cwd for stdio servers. On macOS and Linux it can sandbox local servers to limit their file and network access, configured with a sandbox section in the same file.

Risky vs safer: the differences side by side

FieldRiskySafer
Packagenpx -y packagenpx -y package@1.2.3, a version someone reviewed
File access/ or the home folderOne project folder, such as ./docs
SecretsToken typed into env or headers${VAR}, ${env:VAR} or ${input:id}
Remote URLhttp:// on an unknown hosthttps:// on the vendor's own domain
TransportLocal server listening on HTTP with no tokenstdio for local servers, as the MCP spec recommends
ReviewEdited directly, never reviewedChanges go through a pull request

What to check before you commit an mcp.json

A project file is shared by design, so a mistake in it is shared too. Before an mcp.json change is merged, a reviewer should be able to answer five questions from the diff alone.

First, who publishes each package, and does that match the name? A server called after a well-known company but published by someone else is the pattern behind every malicious MCP server reported so far. Second, is every package pinned to an exact version that someone has looked at? Third, does each server get only the folders, scopes and tokens its job needs? Fourth, does any value look like a real secret rather than a reference to one? Scanning the diff for strings that start like known token formats catches most slips. Fifth, is every remote URL HTTPS on a domain the team recognizes?

If the answer to any of these is unclear, the server can still be approved, but someone should decide that knowingly and say so in the pull request. That written decision is also what an auditor asks for when they want to know how MCP servers are approved. Look up any server you are unsure about on the NoFly list before approving it.

Copying an mcp.json example between tools

A common source of broken configs is moving an entry from one tool to another. Change mcpServers to servers when moving into VS Code, and add a type to every entry. Change the variable syntax: ${VAR} in Claude Code becomes ${env:VAR} in Cursor and an input or environment reference in VS Code. Check the path variables too, since ${workspaceFolder} means nothing to Claude Code. After moving an entry, restart the tool and confirm the server is listed: claude mcp list in Claude Code, the MCP section of Cursor's settings, and the server list in VS Code. A server that fails quietly is easy to miss, and one that loads with a literal placeholder instead of its token can fail in confusing ways.

The same server can also appear in several tools at once, because VS Code can be set to read the Cursor and Claude Desktop files. When you need to find MCP servers on developer machines, check every one of these files, not just the one you expect.

Next step: check the mcp.json example you already have

The safest mcp.json example is the one that matches what is actually on your team's machines. Look up any server in your file on the NoFly list, or use the MCP security scanner to read every config file across your org and score each server it finds.

mcp.json questions

What does an mcp.json example look like for Claude Code?

A project .mcp.json has one top-level key, mcpServers, with one entry per server. A local server uses command, args and env; a remote server uses type "http", url and headers. Claude Code expands ${VAR} and ${VAR:-default} in those fields, so secrets can stay in the environment.

Why does VS Code use "servers" instead of "mcpServers"?

VS Code has its own format for .vscode/mcp.json, with a top-level servers object and an optional inputs array for values it prompts you for. Claude Code and Cursor use mcpServers. Copying an entry between them without changing the key is a common reason a server does not load.

How do I keep API keys out of mcp.json?

Reference them instead of typing them. Claude Code uses ${API_KEY}, Cursor uses ${env:API_KEY}, and VS Code can prompt with ${input:id} and a password-type input. VS Code’s documentation puts it plainly: a personal configuration file is not a secret store.

Should mcp.json be committed to git?

Project files like .mcp.json and .cursor/mcp.json are meant to be shared, so commit them, but only with references to secrets, never the values. Review changes to them in pull requests, because every server listed arrives on every machine that clones the repository.

Sources

  1. Claude Code Docs: Connect Claude Code to tools via MCP (.mcp.json format, variable expansion)
  2. Cursor Docs: Model Context Protocol (mcp.json fields, interpolation)
  3. Visual Studio Code Docs: MCP configuration reference
  4. Model Context Protocol: Connect to local MCP servers (filesystem server, permissions warning)
  5. Model Context Protocol: Security best practices

Related reading

Check every mcp.json in your org, not just this one.