<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Socketlist]]></title><description><![CDATA[Socketlist]]></description><link>https://socketlist.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Tue, 27 Oct 2026 05:14:53 GMT</lastBuildDate><atom:link href="https://socketlist.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Windsurf MCP setup in 2026: mcp_config.json, serverUrl and keeping tokens out of the file]]></title><description><![CDATA[Where Cascade's config moved to, how local and remote servers differ, ${env:} interpolation, and how to stay under the 100-tool limit]]></description><link>https://socketlist.hashnode.dev/windsurf-mcp-setup-in-2026-mcp-config-json-serverurl-and-keeping-tokens-out-of-the-file</link><guid isPermaLink="true">https://socketlist.hashnode.dev/windsurf-mcp-setup-in-2026-mcp-config-json-serverurl-and-keeping-tokens-out-of-the-file</guid><category><![CDATA[mcp]]></category><category><![CDATA[windsurf]]></category><category><![CDATA[AI]]></category><category><![CDATA[Developer Tools]]></category><category><![CDATA[Productivity]]></category><dc:creator><![CDATA[Socketlist]]></dc:creator><pubDate>Thu, 15 Oct 2026 20:27:07 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p><strong>Disclosure:</strong> this article is from Socketlist, a free, independent MCP directory. It is adapted from our <a href="https://socketlist.pages.dev/guides/install-mcp-windsurf/">Windsurf MCP guide</a>, which was checked against the official Cascade MCP docs on 2026-10-15.</p>
</blockquote>
<p>Windsurf's Cascade agent speaks MCP well, but two things trip people up: the config file has moved since Windsurf became part of Devin Desktop, and remote servers use a field name other clients don't. Here's a working setup from scratch.</p>
<h2>Step 1: open the right <code>mcp_config.json</code></h2>
<p>Don't hunt for the file by hand. In the Cascade panel, click the <strong>…</strong> (Actions) menu at the top right and choose <strong>Open MCP config file</strong> in the MCPs section. That opens the file Cascade actually reads.</p>
<p>For reference, current docs put it at:</p>
<table>
<thead>
<tr>
<th>OS</th>
<th>Path</th>
</tr>
</thead>
<tbody><tr>
<td>macOS / Linux</td>
<td><code>~/.config/devin/mcp_config.json</code></td>
</tr>
<tr>
<td>Windows</td>
<td><code>%APPDATA%\devin\mcp_config.json</code></td>
</tr>
<tr>
<td>Older Windsurf releases</td>
<td><code>~/.codeium/windsurf/mcp_config.json</code></td>
</tr>
</tbody></table>
<p>If you edited the old <code>~/.codeium</code> path after upgrading and nothing changed, that's why.</p>
<h2>Step 2: add a local (stdio) server</h2>
<p>Local servers use the same <code>mcpServers</code> shape as Claude Desktop and Cursor, so existing snippets carry over. Here's the GitHub reference server:</p>
<pre><code class="language-json">{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "&lt;YOUR_TOKEN&gt;"
      }
    }
  }
}
</code></pre>
<p>Cascade starts <code>npx</code> as a child process. If Node isn't on the PATH that Windsurf inherits (common on macOS when Node comes from a version manager), use the absolute path to <code>npx</code>.</p>
<h2>Step 3: add a remote server with <code>serverUrl</code></h2>
<p>Remote HTTP servers don't need a command. Windsurf uses <code>serverUrl</code> (it also accepts <code>url</code>). Here's the free Socketlist server, which lets Cascade search an MCP directory:</p>
<pre><code class="language-json">{
  "mcpServers": {
    "socketlist": {
      "serverUrl": "https://socketlist.pages.dev/mcp"
    }
  }
}
</code></pre>
<p>If a server needs an API key, add a <code>headers</code> object next to <code>serverUrl</code>, for example <code>"headers": {"Authorization": "Bearer ..."}</code>. (Better: see step 4.)</p>
<p>Before adding a remote server, you can check that it answers at all:</p>
<pre><code class="language-bash">$ curl -s -X POST https://socketlist.pages.dev/mcp \
    -H 'content-type: application/json' \
    -H 'accept: application/json, text/event-stream' \
    -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
  | jq -r '.result.tools[].name'
search_servers
get_server
list_categories
best_for
</code></pre>
<p>Those four names are what Cascade will list under the server once it connects.</p>
<h2>Step 4: keep secrets out of the file</h2>
<p>This is the feature most people miss. <code>mcp_config.json</code> supports interpolation in <code>command</code>, <code>args</code>, <code>env</code>, <code>serverUrl</code>, <code>url</code> and <code>headers</code>:</p>
<ul>
<li><code>${env:VAR_NAME}</code> reads an environment variable</li>
<li><code>${file:/path/to/file}</code> reads the contents of a file</li>
</ul>
<p>So the GitHub token never has to sit in the config:</p>
<pre><code class="language-json">{
  "mcpServers": {
    "socketlist": { "serverUrl": "https://socketlist.pages.dev/mcp" },
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "${env:GITHUB_TOKEN}" },
      "disabledTools": ["create_repository"]
    }
  }
}
</code></pre>
<p>Now the file is safe to share or back up. Export <code>GITHUB_TOKEN</code> in your shell profile and give the token the smallest scopes that work.</p>
<p>A quick sanity check that both servers are registered and of the right kind:</p>
<pre><code class="language-bash">$ jq -r '.mcpServers | to_entries[] | "\(.key)\t\(.value.serverUrl // .value.command)"' mcp_config.json
socketlist	https://socketlist.pages.dev/mcp
github	npx
</code></pre>
<p>And if you broke the JSON while editing (trailing commas are the classic):</p>
<pre><code class="language-bash">$ echo '{"mcpServers":{"a":{"command":"npx",}}}' &gt; bad.json
$ python3 -m json.tool bad.json
Illegal trailing comma before end of object: line 1 column 36 (char 35)
</code></pre>
<h2>Step 5: switch servers and single tools off</h2>
<p>The MCPs section of the Actions menu lists each server with a toggle. That's the fastest way to switch a server off for a session without deleting its config.</p>
<p>For finer control, the <code>disabledTools</code> array in the example above hides specific tools from one server. That matters because <strong>Cascade can use at most 100 tools at a time</strong>. A couple of large servers (GitHub alone exposes dozens of tools) can get you there quickly. Disabling tools you never use keeps you under the limit. It also makes the agent's choices more predictable, since it has fewer similar tools to pick from.</p>
<p>Good candidates to disable are destructive or rare actions you'd rather do by hand: creating repositories, deleting branches, merging.</p>
<h2>A note on Devin Local</h2>
<p>All of this applies to the <strong>Cascade</strong> agent. The newer Devin Local agent keeps its MCP servers in the Devin CLI config instead, so servers added in <code>mcp_config.json</code> won't show up there. If a server "disappears" after you switch agents, that's the reason.</p>
<h2>Recap</h2>
<ol>
<li>Open the config from <strong>Cascade → … → Open MCP config file</strong>.</li>
<li>Local servers: <code>command</code> + <code>args</code> under <code>mcpServers</code>.</li>
<li>Remote servers: <code>serverUrl</code> (or <code>url</code>), plus <code>headers</code> if needed.</li>
<li>Secrets via <code>${env:VAR}</code> or <code>${file:/path}</code>.</li>
<li>Toggle servers in the MCPs list, and trim tools with <code>disabledTools</code> to stay under 100.</li>
</ol>
<p>The <a href="https://socketlist.pages.dev/guides/install-mcp-windsurf/">full Windsurf guide</a> stays updated as the docs change. If you want tested configs to paste in, the <a href="https://socketlist.pages.dev/free/socketlist-free-sample.zip">free Socketlist sample pack</a> is a plain zip, no signup.</p>
]]></content:encoded></item><item><title><![CDATA[Letting Claude or Cursor query your database over MCP without regretting it]]></title><description><![CDATA[Which open-source database MCP servers exist for Postgres, MySQL, MongoDB and SQL Server, and a read-only setup you can copy]]></description><link>https://socketlist.hashnode.dev/letting-claude-or-cursor-query-your-database-over-mcp-without-regretting-it</link><guid isPermaLink="true">https://socketlist.hashnode.dev/letting-claude-or-cursor-query-your-database-over-mcp-without-regretting-it</guid><category><![CDATA[mcp]]></category><category><![CDATA[PostgreSQL]]></category><category><![CDATA[Databases]]></category><category><![CDATA[AI]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Socketlist]]></dc:creator><pubDate>Thu, 15 Oct 2026 20:26:12 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p><strong>Disclosure:</strong> this article is from Socketlist, a free, independent MCP directory. It is adapted from our <a href="https://socketlist.pages.dev/guides/database-mcp-servers/">database MCP servers guide</a>. Star counts were taken from GitHub on 2026-10-15 and change daily.</p>
</blockquote>
<p>Asking an AI assistant "which customers signed up last week but never activated?" and getting the answer from your actual database is one of the most useful things MCP does. It's also one of the easiest ways to drop a table by accident. This post covers which servers to look at and how to set them up so the worst case is a slow query, not lost data.</p>
<h2>The landscape, grouped by engine</h2>
<p>From the servers we track, here are the ones worth starting with:</p>
<p><strong>Multi-database (one server, several engines)</strong></p>
<ul>
<li><a href="https://github.com/bytebase/dbhub"><code>bytebase/dbhub</code></a>: about 3.6k stars. A universal gateway if you hop between Postgres, MySQL and others.</li>
<li><a href="https://github.com/clidey/whodb"><code>clidey/whodb</code></a>: about 5k stars. A database explorer that also speaks MCP.</li>
<li><a href="https://github.com/executeautomation/mcp-database-server"><code>executeautomation/mcp-database-server</code></a>: about 390 stars, covering SQLite, SQL Server and Postgres.</li>
</ul>
<p><strong>PostgreSQL</strong></p>
<ul>
<li><a href="https://github.com/subnetmarco/pgmcp"><code>subnetmarco/pgmcp</code></a>: about 540 stars, natural-language querying for Postgres.</li>
<li>Also in the guide: <code>call518/mcp-postgresql-ops</code> for ops and monitoring and <code>timescale/pg-aiguide</code>.</li>
</ul>
<p><strong>MySQL</strong></p>
<ul>
<li><a href="https://github.com/designcomputer/mysql_mcp_server"><code>designcomputer/mysql_mcp_server</code></a>: about 1.4k stars.</li>
</ul>
<p><strong>MongoDB</strong></p>
<ul>
<li><a href="https://github.com/mongodb-js/mongodb-mcp-server"><code>mongodb-js/mongodb-mcp-server</code></a>: about 1.1k stars, maintained by MongoDB.</li>
</ul>
<p><strong>SQL Server and Azure</strong></p>
<ul>
<li><a href="https://github.com/Azure/data-api-builder"><code>Azure/data-api-builder</code></a>: about 1.5k stars, from Microsoft.</li>
<li><a href="https://github.com/Aaronontheweb/mssql-mcp"><code>Aaronontheweb/mssql-mcp</code></a>: about 160 stars.</li>
</ul>
<p>Rule of thumb: if you only use one engine, pick a server for that engine. Use a multi-database server when switching configs is the bigger pain.</p>
<h2>Rule 1: a read-only user, every time</h2>
<p>The assistant can only do what the database user allows. So create a user for it that can't write. In PostgreSQL:</p>
<pre><code class="language-sql">CREATE ROLE mcp_readonly WITH LOGIN PASSWORD 'change-me';
GRANT CONNECT ON DATABASE app TO mcp_readonly;
GRANT USAGE ON SCHEMA public TO mcp_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO mcp_readonly;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO mcp_readonly;
</code></pre>
<p>Check that writes really fail before you connect any assistant:</p>
<pre><code class="language-bash">$ psql "postgresql://mcp_readonly:change-me@localhost/app" -c "DELETE FROM users;"
ERROR:  permission denied for table users
</code></pre>
<p>If that <code>DELETE</code> succeeds, stop and fix the grants. The equivalent in MySQL is <code>GRANT SELECT ON app.* TO 'mcp_readonly'@'%';</code>, and in MongoDB a user with only the built-in <code>read</code> role.</p>
<p>Start read-only even when you plan to allow writes later. You'll learn what the assistant actually asks for, and you can grant narrow write rights afterwards.</p>
<h2>Rule 2: staging first</h2>
<p>Point the server at a copy or a staging database for the first sessions. The assistant will sometimes run expensive queries (full scans, big joins) while it explores the schema, and you don't want to find that out on production at peak time. A <code>pg_dump</code> into a local container is enough.</p>
<h2>Rule 3: connection strings go in <code>env</code>, not in prompts</h2>
<p>Don't paste a connection string into chat ("connect to postgres://admin:..."). It ends up in conversation history. Put it in the <code>env</code> block of the server's config, which the client passes to the process and doesn't show to the model:</p>
<pre><code class="language-json">{
  "mcpServers": {
    "postgres": {
      "command": "npx",
      "args": ["-y", "@bytebase/dbhub"],
      "env": {
        "DSN": "postgresql://mcp_readonly:change-me@localhost:5432/app"
      }
    }
  }
}
</code></pre>
<p>(Each server has its own flag or variable name, <code>DSN</code> here. Check the README of the one you choose.) If the file lives in a repo, keep it out of git or use your client's environment-variable substitution so the secret stays in your shell profile.</p>
<p>Before you restart the client, confirm the file parses and the server is registered:</p>
<pre><code class="language-bash">$ python3 -m json.tool ~/.cursor/mcp.json &gt; /dev/null &amp;&amp; echo ok
ok
$ jq -r '.mcpServers | keys[]' ~/.cursor/mcp.json
postgres
</code></pre>
<h2>Rule 4: approve, don't auto-run</h2>
<p>Most clients let you approve each tool call. For database tools, keep approval on, at least until you've seen what kind of SQL the assistant writes for your schema. Reading a proposed query takes five seconds. Restoring a table takes much longer.</p>
<h2>What it looks like when it works</h2>
<p>Once connected, a good first prompt is "list the tables and their row counts". It's harmless and confirms the connection. Then try something you'd normally write by hand: "average order value by month for 2026, as a table". The assistant reads the schema, writes the SQL, runs it as your read-only user and shows the result. Because the user can't write, a misunderstood request just fails with a permission error.</p>
<h2>Checklist</h2>
<ul>
<li> Dedicated database user with <code>SELECT</code> only</li>
<li> Verified that a write fails with that user</li>
<li> Staging or copy first</li>
<li> Credentials in <code>env</code>, not in prompts or git</li>
<li> Tool-call approval left on</li>
</ul>
<h2>More</h2>
<p>The <a href="https://socketlist.pages.dev/guides/database-mcp-servers/">full guide</a> has per-engine lists (PostgreSQL, MySQL, MongoDB, SQL Server), with live star counts and last-push dates. Our security checklist covers general MCP hygiene beyond databases.</p>
<p>For tested starter configs, grab the <a href="https://socketlist.pages.dev/free/socketlist-free-sample.zip">free Socketlist sample pack</a>. It's a plain zip, no signup.</p>
]]></content:encoded></item><item><title><![CDATA[mcpServers or servers? A side-by-side map of MCP config files in Claude Desktop, Cursor, VS Code and Windsurf]]></title><description><![CDATA[Where each client keeps its MCP config, which top-level key it expects, how remote URLs are written, and a 10-second jq check that catches the usual…]]></description><link>https://socketlist.hashnode.dev/mcpservers-or-servers-a-side-by-side-map-of-mcp-config-files-in-claude-desktop-cursor-vs-code-and-windsurf</link><guid isPermaLink="true">https://socketlist.hashnode.dev/mcpservers-or-servers-a-side-by-side-map-of-mcp-config-files-in-claude-desktop-cursor-vs-code-and-windsurf</guid><category><![CDATA[mcp]]></category><category><![CDATA[AI]]></category><category><![CDATA[cursor]]></category><category><![CDATA[Developer Tools]]></category><category><![CDATA[ vscode]]></category><dc:creator><![CDATA[Socketlist]]></dc:creator><pubDate>Thu, 15 Oct 2026 20:24:32 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p><strong>Disclosure:</strong> this article is from Socketlist, a free, independent MCP directory. It is adapted from our <a href="https://socketlist.pages.dev/guides/mcp-config-file-locations/">MCP config file locations guide</a>.</p>
</blockquote>
<p>The most common reason an MCP server "doesn't show up" isn't the server. It's the config file: wrong path, wrong top-level key, or a remote URL written in a field the client ignores. Every client reads JSON, but they don't all read the <em>same</em> JSON. Here's the map we use, plus a couple of shell checks to confirm a file is right before you restart anything.</p>
<h2>The quick reference</h2>
<table>
<thead>
<tr>
<th>Client</th>
<th>File</th>
<th>Top-level key</th>
<th>Remote server field</th>
</tr>
</thead>
<tbody><tr>
<td>Claude Desktop</td>
<td><code>claude_desktop_config.json</code> (Settings → Developer → Edit Config)</td>
<td><code>mcpServers</code></td>
<td>Remote servers are added as connectors in the app</td>
</tr>
<tr>
<td>Cursor</td>
<td><code>~/.cursor/mcp.json</code> (global) / <code>.cursor/mcp.json</code> (project)</td>
<td><code>mcpServers</code></td>
<td><code>url</code></td>
</tr>
<tr>
<td>VS Code</td>
<td><code>.vscode/mcp.json</code> (also <code>.mcp.json</code>)</td>
<td><code>servers</code></td>
<td><code>"type": "http"</code> + <code>url</code></td>
</tr>
<tr>
<td>Windsurf (Cascade)</td>
<td><code>mcp_config.json</code> (Cascade → … → Open MCP config file)</td>
<td><code>mcpServers</code></td>
<td><code>serverUrl</code> / <code>url</code></td>
</tr>
</tbody></table>
<p>Two lessons hide in that table:</p>
<ol>
<li><strong>VS Code is the odd one out.</strong> In <code>.vscode/mcp.json</code> the root key is <code>servers</code>, not <code>mcpServers</code>, and every entry gets a <code>type</code>.</li>
<li><strong>Remote fields differ.</strong> Cursor uses <code>url</code>, Windsurf prefers <code>serverUrl</code> (it also accepts <code>url</code>), VS Code wants <code>type: "http"</code> with <code>url</code>, and Claude Desktop handles remote servers as connectors in the app instead of in the JSON file.</li>
</ol>
<h2>The shared <code>mcpServers</code> format</h2>
<p>Claude Desktop, Cursor and Windsurf all accept the same local (stdio) entry. Here's the official Fetch reference server, launched with <code>uvx</code>:</p>
<pre><code class="language-json">{
  "mcpServers": {
    "fetch": {
      "command": "uvx",
      "args": ["mcp-server-fetch"]
    }
  }
}
</code></pre>
<p>The client starts <code>command</code> with <code>args</code> as a child process and talks to it over stdin/stdout. If <code>uvx</code> isn't on the PATH the client sees, the server fails silently, which is why many people write the full path to the binary.</p>
<p>You can paste that block into any of the three clients unchanged. That's the best reason to keep one "source" snippet per server and copy it around.</p>
<h2>VS Code: <code>servers</code> plus <code>type</code></h2>
<p>VS Code's workspace file looks similar but isn't the same:</p>
<pre><code class="language-json">{
  "servers": {
    "fetch": {
      "type": "stdio",
      "command": "uvx",
      "args": ["mcp-server-fetch"]
    },
    "socketlist": {
      "type": "http",
      "url": "https://socketlist.pages.dev/mcp"
    }
  }
}
</code></pre>
<p>Local servers say <code>"type": "stdio"</code>, remote ones <code>"type": "http"</code> with a <code>url</code>. If you copy a Cursor snippet into <code>.vscode/mcp.json</code> and keep <code>mcpServers</code>, VS Code won't find anything.</p>
<h2>Check the file before you restart the client</h2>
<p>Restarting an editor to test JSON is slow. Ask <code>jq</code> instead. With the Cursor/Claude-style file saved as <code>c.json</code>:</p>
<pre><code class="language-bash">$ jq -r '.mcpServers | keys[]' c.json
fetch
</code></pre>
<p>For the VS Code file (<code>v.json</code>), list each server and its transport:</p>
<pre><code class="language-bash">$ jq -r '.servers | to_entries[] | "\(.key)\t\(.value.type)"' v.json
fetch	stdio
socketlist	http
</code></pre>
<p>Now the mistake from above. Run the <code>mcpServers</code> query against the VS Code file and you get:</p>
<pre><code class="language-bash">$ jq -r '.mcpServers | keys[]' v.json
jq: error (at v.json:1): null (null) has no keys
</code></pre>
<p>That error means "this file has no <code>mcpServers</code> key". It's exactly what a client sees when you used the wrong root key.</p>
<p>Syntax errors are the other classic problem, and a trailing comma is the usual one. Python's built-in validator names the exact spot:</p>
<pre><code class="language-bash">$ echo '{"mcpServers":{"a":{"command":"npx",}}}' &gt; bad.json
$ python3 -m json.tool bad.json
Illegal trailing comma before end of object: line 1 column 36 (char 35)
</code></pre>
<p>JSON doesn't allow comments or trailing commas, even though most editors won't complain while you type.</p>
<h2>Test a remote server without any client</h2>
<p>For an HTTP server you can skip the client and call the endpoint directly. MCP is JSON-RPC, so <code>tools/list</code> shows what the server offers:</p>
<pre><code class="language-bash">$ curl -s -X POST https://socketlist.pages.dev/mcp \
    -H 'content-type: application/json' \
    -H 'accept: application/json, text/event-stream' \
    -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
  | jq -r '.result.tools[].name'
search_servers
get_server
list_categories
best_for
</code></pre>
<p>If that returns tool names, the server works and any remaining problem is in your client config. If it doesn't, no amount of config editing will help.</p>
<h2>Global vs project files</h2>
<p>Cursor and VS Code both support a config file per project (<code>.cursor/mcp.json</code>, <code>.vscode/mcp.json</code>). Commit these when a team shares a server, but never commit tokens. Use the client's environment-variable support or an <code>env</code> block that reads from your shell. Global files like <code>~/.cursor/mcp.json</code> suit personal servers you want in every project.</p>
<h2>A short troubleshooting order</h2>
<ol>
<li><strong>Right file?</strong> Open it from the client's own menu (Claude Desktop: Settings → Developer → Edit Config; Windsurf: Cascade → … → Open MCP config file). Then you know you're editing the file it actually reads.</li>
<li><strong>Valid JSON?</strong> <code>python3 -m json.tool file.json</code>.</li>
<li><strong>Right root key?</strong> <code>mcpServers</code> everywhere except VS Code's <code>servers</code>.</li>
<li><strong>Right remote field?</strong> <code>url</code>, <code>serverUrl</code>, or <code>type: "http"</code> + <code>url</code>, depending on the client.</li>
<li><strong>Does the server itself work?</strong> <code>curl</code> the endpoint or run the stdio command by hand in a terminal.</li>
<li><strong>Fully restart</strong> the client. Many only read MCP config at startup.</li>
</ol>
<h2>Going further</h2>
<p>The <a href="https://socketlist.pages.dev/guides/mcp-config-file-locations/">full guide</a> links to step-by-step setup for Claude Desktop, Cursor, VS Code, Windsurf, Claude Code and Codex. There's also a config generator that writes the right shape for each client.</p>
<p>If you want ready-made, tested configs to start from, the <a href="https://socketlist.pages.dev/free/socketlist-free-sample.zip">free Socketlist sample pack</a> is a plain zip, no signup.</p>
]]></content:encoded></item><item><title><![CDATA[Claude Code MCP scopes explained: .mcp.json vs claude mcp add (with outputs)]]></title><description><![CDATA[Where each scope writes, which one wins, how to keep secrets out of git, and scripts that work on Windows]]></description><link>https://socketlist.hashnode.dev/claude-code-mcp-scopes-explained-mcp-json-vs-claude-mcp-add-with-outputs</link><guid isPermaLink="true">https://socketlist.hashnode.dev/claude-code-mcp-scopes-explained-mcp-json-vs-claude-mcp-add-with-outputs</guid><category><![CDATA[claude]]></category><category><![CDATA[AI]]></category><category><![CDATA[Developer Tools]]></category><category><![CDATA[mcp]]></category><dc:creator><![CDATA[Socketlist]]></dc:creator><pubDate>Thu, 15 Oct 2026 07:02:33 GMT</pubDate><content:encoded><![CDATA[<p>Claude Code can load MCP servers from three scopes, and the same server name can exist in more than one. This is a short, tested reference: what each scope writes, which one wins, and how to share configs without sharing keys. Tested with <strong>Claude Code 2.1.300</strong>; behaviour quoted from the official docs at <a href="https://code.claude.com/docs/en/mcp">https://code.claude.com/docs/en/mcp</a>.</p>
<h2>The three scopes</h2>
<table>
<thead>
<tr>
<th>Scope</th>
<th>Flag</th>
<th>Stored in</th>
<th>Who sees it</th>
</tr>
</thead>
<tbody><tr>
<td>Local (default)</td>
<td>none, or <code>--scope local</code></td>
<td><code>~/.claude.json</code>, under the current project</td>
<td>Only you, only in this project</td>
</tr>
<tr>
<td>Project</td>
<td><code>--scope project</code></td>
<td><code>.mcp.json</code> in the project root</td>
<td>Everyone who clones the repo</td>
</tr>
<tr>
<td>User</td>
<td><code>--scope user</code></td>
<td><code>~/.claude.json</code>, top level</td>
<td>Only you, in all projects</td>
</tr>
</tbody></table>
<h2>What the CLI actually writes</h2>
<p>Local scope (no flag):</p>
<pre><code class="language-bash">claude mcp add --transport http n8n-tools http://localhost:5678/mcp/utility-tools
</code></pre>
<pre><code>Added HTTP MCP server n8n-tools with URL: http://localhost:5678/mcp/utility-tools to local config
File modified: ~/.claude.json [project: /path/to/project]
</code></pre>
<p>The entry under that project in <code>~/.claude.json</code>:</p>
<pre><code class="language-json">{"n8n-tools": {"type": "http", "url": "http://localhost:5678/mcp/utility-tools"}}
</code></pre>
<p>User scope, stdio server:</p>
<pre><code class="language-bash">claude mcp add --scope user memory -- npx -y @modelcontextprotocol/server-memory
</code></pre>
<p>Top-level <code>mcpServers</code> in <code>~/.claude.json</code>:</p>
<pre><code class="language-json">{"memory": {"type": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-memory"], "env": {}}}
</code></pre>
<p>Project scope:</p>
<pre><code class="language-bash">claude mcp add --scope project time -- uvx mcp-server-time
</code></pre>
<p><code>.mcp.json</code>:</p>
<pre><code class="language-json">{
  "mcpServers": {
    "time": {
      "type": "stdio",
      "command": "uvx",
      "args": [
        "mcp-server-time"
      ],
      "env": {}
    }
  }
}
</code></pre>
<p>Two details worth knowing: everything after <code>--</code> is the server command and its arguments, so options like <code>--scope</code> and <code>--env</code> must come <strong>before</strong> the server name; and the CLI writes <code>"type": "stdio"</code> and an empty <code>"env": {}</code> even when you don't set any. A hand-written <code>.mcp.json</code> without those two fields is equivalent.</p>
<h2>Which definition wins</h2>
<p>From the docs: when the same server is defined in more than one place, Claude Code connects once, using the <strong>highest-precedence</strong> source, and uses that entire entry (fields are not merged):</p>
<ol>
<li>Local</li>
<li>Project</li>
<li>User</li>
<li>Plugin-provided servers</li>
<li>claude.ai connectors</li>
</ol>
<p>So a teammate's <code>.mcp.json</code> entry can be overridden for yourself by adding a local server with the same name, without editing the shared file.</p>
<h2>Project servers need approval</h2>
<p>Claude Code asks for approval before using servers from a project's <code>.mcp.json</code>. Until you approve, <code>claude mcp list</code> shows them as <code>â¸ Pending approval (run claude to approve)</code>. To undo your choices: <code>claude mcp reset-project-choices</code>.</p>
<h2>Keep keys out of <code>.mcp.json</code></h2>
<p><code>.mcp.json</code> supports environment variable expansion in <code>command</code>, <code>args</code>, <code>env</code>, <code>url</code> and headers:</p>
<ul>
<li><code>${VAR}</code>: the value of <code>VAR</code></li>
<li><code>${VAR:-default}</code>: <code>VAR</code> if set, otherwise <code>default</code></li>
</ul>
<pre><code class="language-json">{
  "mcpServers": {
    "brave-search": {
      "command": "npx",
      "args": ["-y", "@brave/brave-search-mcp-server", "--transport", "stdio"],
      "env": { "BRAVE_API_KEY": "${BRAVE_API_KEY}" }
    }
  }
}
</code></pre>
<p>Commit the file, and each person sets <code>BRAVE_API_KEY</code> in their own environment. If the variable isn't set and has no default, <code>claude mcp list</code> warns about it.</p>
<h2>Checking what's connected</h2>
<pre><code class="language-bash">claude mcp list
</code></pre>
<pre><code>n8n-tools: http://localhost:5678/mcp/utility-tools (HTTP) - âœ” Connected
</code></pre>
<p><code>claude mcp get &lt;name&gt;</code> also shows the scope (<code>Scope: Local config (private to you in this project)</code>) and the exact command to remove it, for example <code>claude mcp remove n8n-tools -s local</code>.</p>
<h2>Scripts that work on macOS, Linux and Windows</h2>
<p>If you set up the same servers on several machines, a script is easier than copying JSON. A Bash version with a configurable scope:</p>
<pre><code class="language-bash">#!/usr/bin/env bash
set -euo pipefail
SCOPE="${SCOPE:-local}"
claude mcp add --transport stdio --scope "$SCOPE" memory -- npx -y @modelcontextprotocol/server-memory
claude mcp add --transport http --scope "$SCOPE" context7 https://mcp.context7.com/mcp
</code></pre>
<p>The PowerShell equivalent:</p>
<pre><code class="language-powershell">param([ValidateSet('local','user','project')][string]$Scope='local')
$ErrorActionPreference='Stop'
claude mcp add --transport stdio --scope $Scope memory -- npx -y @modelcontextprotocol/server-memory
if ($LASTEXITCODE -ne 0) { throw 'claude mcp add failed' }
claude mcp add --transport http --scope $Scope context7 https://mcp.context7.com/mcp
if ($LASTEXITCODE -ne 0) { throw 'claude mcp add failed' }
</code></pre>
<p>We ran both with <code>--scope project</code> on the same machine (Bash, and PowerShell 7 on Linux) and compared the resulting <code>.mcp.json</code> files: identical. PowerShell needs the explicit <code>$LASTEXITCODE</code> check because a failing native command doesn't stop the script on its own. In PowerShell, quote arguments with single quotes if they contain spaces or special characters, for example <code>--header 'Authorization: Bearer YOUR_TOKEN'</code>.</p>
<p>Running a script twice fails on the first server that already exists. Remove it first with <code>claude mcp remove &lt;name&gt; -s &lt;scope&gt;</code>.</p>
<hr />
<p><em>Disclosure: I run Socketlist, an open directory of MCP servers with install configs checked against official docs, and sell a paid Claude Code starter pack there. Everything in this article is free to use.</em></p>
<ul>
<li>Free sample (Essentials <code>.mcp.json</code> plus the Bash and PowerShell scripts for six servers that need no API keys): <a href="https://socketlist.pages.dev/free/socketlist-free-sample.zip">https://socketlist.pages.dev/free/socketlist-free-sample.zip</a></li>
<li>Claude Code + MCP Starter Pack ($12): <a href="https://socketlist.pages.dev/shop/claude-code-mcp-starter-pack/">https://socketlist.pages.dev/shop/claude-code-mcp-starter-pack/</a></li>
</ul>
]]></content:encoded></item></channel></rss>