<?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[Morpheon]]></title><description><![CDATA[Morpheon]]></description><link>https://morpheon.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Tue, 27 Oct 2026 04:08:59 GMT</lastBuildDate><atom:link href="https://morpheon.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to Test an MCP Server Before Connecting Claude or Cursor]]></title><description><![CDATA[You can write a small MCP server in an afternoon and still lose the next hour to a silent failure in Claude Desktop or Cursor. The host starts your…]]></description><link>https://morpheon.hashnode.dev/how-to-test-an-mcp-server-before-connecting-claude-or-cursor</link><guid isPermaLink="true">https://morpheon.hashnode.dev/how-to-test-an-mcp-server-before-connecting-claude-or-cursor</guid><category><![CDATA[AI]]></category><category><![CDATA[Testing]]></category><category><![CDATA[claude]]></category><category><![CDATA[cursor]]></category><category><![CDATA[mcp]]></category><dc:creator><![CDATA[Lee]]></dc:creator><pubDate>Sun, 25 Oct 2026 07:21:46 GMT</pubDate><content:encoded><![CDATA[<p>You can write a small MCP server in an afternoon and still lose the next hour to a silent failure in Claude Desktop or Cursor. The host starts your process, talks JSON-RPC over stdio, and if anything on stdout is not protocol traffic, the connection looks "broken" with almost no useful error.</p>
<p>This post is the pre-connect checklist: run the server alone, inspect tools with MCP Inspector, then paste a host config that uses absolute paths.</p>
<p>If you need a starter project, the free <a href="https://mcp-server-generator-pearl.vercel.app/">MCP Server Generator</a> builds a TypeScript or Python ZIP from a form, and the <a href="https://github.com/wuzzheng/mcp-server-template">mcp-server-template</a> repo has the same layout.</p>
<h2>1. Start the server in a terminal</h2>
<p>From the project folder:</p>
<pre><code class="language-bash"># TypeScript
npm install
npm run build
node dist/index.js
</code></pre>
<pre><code class="language-bash"># Python
pip install -r requirements.txt
python server.py
</code></pre>
<p>A healthy stdio server sits quiet. It does not print a banner. It waits for JSON-RPC on stdin. If you see logs on stdout, redirect them to stderr before you connect a host.</p>
<h2>2. List tools with MCP Inspector</h2>
<p>Install and run the official inspector against your command:</p>
<pre><code class="language-bash">npx @modelcontextprotocol/inspector node /absolute/path/to/dist/index.js
</code></pre>
<p>Confirm <code>tools/list</code> returns your tools with the expected names and input schemas. Call one tool with sample arguments. Fix schema typos here, not inside Claude.</p>
<h2>3. Connect with absolute paths</h2>
<p>Hosts do not inherit your shell <code>PATH</code> or working directory. Prefer absolute paths in <code>args</code>.</p>
<p>Claude Desktop (<code>claude_desktop_config.json</code>):</p>
<pre><code class="language-json">{
  "mcpServers": {
    "weather-helper": {
      "command": "node",
      "args": ["/absolute/path/to/dist/index.js"]
    }
  }
}
</code></pre>
<p>Cursor uses the same <code>mcpServers</code> shape in its MCP settings. After saving, fully quit and reopen the app, then ask it to list the tools from <code>weather-helper</code>.</p>
<h2>4. Quick failure table</h2>
<table>
<thead>
<tr>
<th>Symptom</th>
<th>Likely cause</th>
</tr>
</thead>
<tbody><tr>
<td>Server never appears</td>
<td>Relative path, wrong command, or config file not the one the app reads</td>
</tr>
<tr>
<td>Connected but zero tools</td>
<td><code>tools/list</code> empty or name mismatch; re-check Inspector</td>
</tr>
<tr>
<td>Random disconnects</td>
<td>Logs printed to stdout; move logging to stderr</td>
</tr>
<tr>
<td>Works in terminal, fails in host</td>
<td>Missing absolute path or env vars the GUI process does not have</td>
</tr>
</tbody></table>
<p>For the longer walkthrough with Windows path notes, see the on-site guide: <a href="https://mcp-server-generator-pearl.vercel.app/how-to-test-mcp-server">How to test an MCP server</a>.</p>
]]></content:encoded></item><item><title><![CDATA[How to Connect a Custom MCP Server to Claude Desktop and Cursor (with Absolute-Path Configs That Actually Work)]]></title><description><![CDATA[Writing a small MCP server takes a few minutes. Getting Claude Desktop or Cursor to actually start it is where most people lose an hour. The server runs…]]></description><link>https://morpheon.hashnode.dev/how-to-connect-a-custom-mcp-server-to-claude-desktop-and-cursor-with-absolute-path-configs-that-actually-work</link><guid isPermaLink="true">https://morpheon.hashnode.dev/how-to-connect-a-custom-mcp-server-to-claude-desktop-and-cursor-with-absolute-path-configs-that-actually-work</guid><category><![CDATA[mcp]]></category><category><![CDATA[AI]]></category><category><![CDATA[claude]]></category><category><![CDATA[cursor]]></category><dc:creator><![CDATA[Lee]]></dc:creator><pubDate>Sat, 17 Oct 2026 10:09:37 GMT</pubDate><content:encoded><![CDATA[<p>Writing a small MCP server takes a few minutes. Getting Claude Desktop or Cursor to actually start it is where most people lose an hour. The server runs fine in a terminal, but the host shows it as failed, or doesn't show it at all. Almost every time, the cause is the config, not the code.</p>
<p>This post covers only the connection part: where the config files live, a config shape that survives the way desktop apps launch processes, Windows paths, and a troubleshooting table. The examples use a <code>weather-helper</code> project with one <code>get_weather</code> tool. You can create the same project with the free <a href="https://mcp-server-generator-pearl.vercel.app/">MCP Server Generator</a>, or clone the <a href="https://github.com/wuzzheng/mcp-server-template">mcp-server-template repo</a>, which has both a TypeScript and a Python version.</p>
<h2>How a host starts your server</h2>
<p>Claude Desktop and Cursor both read an <code>mcpServers</code> object. Each entry is a <code>command</code> plus <code>args</code>. The host spawns that process and speaks JSON-RPC to it over stdin/stdout (the stdio transport). There's no port and no URL. If the process can't start, or if it prints anything that isn't protocol output to stdout, the connection fails.</p>
<p>Two consequences matter:</p>
<ol>
<li><p>The process is started by a GUI app, <strong>not by your shell</strong>. Your <code>PATH</code> from <code>.zshrc</code> or <code>.bashrc</code> usually isn't there, and neither is your activated virtualenv.</p>
</li>
<li><p>The <strong>working directory is the app's choice</strong>, not your project folder. Relative paths like <code>src/index.ts</code> resolve somewhere else.</p>
</li>
</ol>
<h2>Where the config files are</h2>
<p><strong>Claude Desktop</strong></p>
<table>
<thead>
<tr>
<th>OS</th>
<th>Path</th>
</tr>
</thead>
<tbody><tr>
<td>macOS</td>
<td><code>~/Library/Application Support/Claude/claude_desktop_config.json</code></td>
</tr>
<tr>
<td>Windows</td>
<td><code>%APPDATA%\Claude\claude_desktop_config.json</code></td>
</tr>
</tbody></table>
<p>In the app, Settings → Developer → Edit Config opens the file for you.</p>
<p><strong>Cursor</strong></p>
<table>
<thead>
<tr>
<th>Scope</th>
<th>Path</th>
</tr>
</thead>
<tbody><tr>
<td>Global (all projects)</td>
<td><code>~/.cursor/mcp.json</code> (Windows: <code>%USERPROFILE%\.cursor\mcp.json</code>)</td>
</tr>
<tr>
<td>One project</td>
<td><code>.cursor/mcp.json</code> in the project root</td>
</tr>
</tbody></table>
<p>Both files use the same <code>mcpServers</code> format, so one entry works in either.</p>
<h2>The config that works: absolute paths to the interpreter and the entry file</h2>
<p>Run <code>pwd</code> inside the project folder and substitute it below. These are the same shapes the generator writes into each project's README.</p>
<p><strong>TypeScript</strong> (after <code>npm install</code>). Point <code>command</code> at the project's own <code>tsx</code>, so neither <code>npx</code> nor a global install is involved:</p>
<pre><code class="language-json">{
  "mcpServers": {
    "weather-helper": {
      "command": "/Users/you/mcp/weather-helper/node_modules/.bin/tsx",
      "args": ["/Users/you/mcp/weather-helper/src/index.ts"]
    }
  }
}
</code></pre>
<p>If you prefer compiled JavaScript, run <code>npm run build</code> first, then use <code>"command": "node"</code> with <code>"args": ["/Users/you/mcp/weather-helper/dist/index.js"]</code>. If the host can't find <code>node</code>, use the absolute path that <code>which node</code> prints.</p>
<p><strong>Python</strong> (after creating <code>.venv</code> and running <code>pip install -r requirements.txt</code>). Point <code>command</code> at the venv's interpreter:</p>
<pre><code class="language-json">{
  "mcpServers": {
    "weather-helper": {
      "command": "/Users/you/mcp/weather-helper/.venv/bin/python",
      "args": ["/Users/you/mcp/weather-helper/server.py"]
    }
  }
}
</code></pre>
<p>A bare <code>"python"</code> is the most common Python failure. It resolves to whatever interpreter the app finds first, which usually doesn't have the <code>mcp</code> package installed. If you use uv, this also works, and it installs dependencies from <code>pyproject.toml</code> on first run:</p>
<pre><code class="language-json">{
  "mcpServers": {
    "weather-helper": {
      "command": "uv",
      "args": ["run", "--directory", "/Users/you/mcp/weather-helper", "server.py"]
    }
  }
}
</code></pre>
<h2>The <code>cwd</code> pitfall</h2>
<p>Many examples online look like this:</p>
<pre><code class="language-json">{ "command": "npm", "args": ["start"], "cwd": "/Users/you/mcp/weather-helper" }
</code></pre>
<p>It's tidy, and it works in hosts that honour <code>cwd</code>. Some Claude Desktop builds ignore that field. When that happens, <code>npm start</code> runs in the wrong folder, can't find <code>package.json</code>, and exits immediately. The host reports the server as failed, without much explanation. Absolute paths to the binary and the entry file don't depend on <code>cwd</code>, so they behave the same everywhere.</p>
<h2>Windows paths</h2>
<p>Same idea, different file names:</p>
<ul>
<li><p>TypeScript: <code>C:\Users\you\mcp\weather-helper\node_modules\.bin\tsx.cmd</code></p>
</li>
<li><p>Python: <code>C:\Users\you\mcp\weather-helper\.venv\Scripts\python.exe</code></p>
</li>
</ul>
<p>Inside JSON strings every backslash must be doubled:</p>
<pre><code class="language-json">{
  "mcpServers": {
    "weather-helper": {
      "command": "C:\\Users\\you\\mcp\\weather-helper\\.venv\\Scripts\\python.exe",
      "args": ["C:\\Users\\you\\mcp\\weather-helper\\server.py"]
    }
  }
}
</code></pre>
<h2>Test the server before blaming the host</h2>
<p>The MCP Inspector starts your server the same way a host does, then lets you list and call tools in a browser:</p>
<pre><code class="language-bash"># TypeScript project
npx @modelcontextprotocol/inspector npm start

# Python project
npx @modelcontextprotocol/inspector .venv/bin/python server.py
</code></pre>
<p>Call <code>get_weather</code> with <code>{"city": "Paris"}</code>. The template's placeholder handler echoes the argument back: <code>[get_weather] city="Paris"</code> in TypeScript, <code>[get_weather] city='Paris'</code> in Python. If that works, the server is fine and any remaining problem is in the host config.</p>
<h2>After editing the config</h2>
<ul>
<li><p><strong>Claude Desktop:</strong> quit the app completely (not just the window) and reopen it. It only reads the file at startup.</p>
</li>
<li><p><strong>Cursor:</strong> open the MCP section of Cursor Settings. It lists each server, whether it started, and its tools. Toggle the server off and on after editing.</p>
</li>
</ul>
<h2>Troubleshooting</h2>
<table>
<thead>
<tr>
<th>Symptom</th>
<th>Likely cause</th>
<th>Fix</th>
</tr>
</thead>
<tbody><tr>
<td>No servers load at all</td>
<td>Invalid JSON</td>
<td>One trailing comma disables the whole file. Run it through a JSON validator.</td>
</tr>
<tr>
<td><code>spawn npx ENOENT</code> / command not found</td>
<td>The app doesn't see your shell <code>PATH</code></td>
<td>Use absolute paths (<code>which node</code>, <code>which uv</code>, or the project's <code>node_modules/.bin/tsx</code>).</td>
</tr>
<tr>
<td>Server starts, then disconnects</td>
<td>Something printed to stdout</td>
<td>Log with <code>console.error</code> or <code>print(..., file=sys.stderr)</code>.</td>
</tr>
<tr>
<td><code>ModuleNotFoundError: No module named 'mcp'</code></td>
<td><code>command</code> is a Python without the package</td>
<td>Use <code>.venv/bin/python</code> (or <code>uv run</code>).</td>
</tr>
<tr>
<td>Fails only in Claude Desktop with <code>npm start</code></td>
<td><code>cwd</code> ignored</td>
<td>Switch to the absolute-path config above.</td>
</tr>
<tr>
<td>Tools listed but never called</td>
<td>Vague tool descriptions</td>
<td>Say what the tool does and when to use it; mention it by name to test.</td>
</tr>
</tbody></table>
<p>On macOS, Claude Desktop writes per-server logs to <code>~/Library/Logs/Claude/mcp*.log</code>. On Windows they're under <code>%APPDATA%\Claude\logs</code>. Your server's stderr ends up there too, which is often the quickest way to see the real error.</p>
<h2>Summary</h2>
<p>Treat the host as a process launcher with no shell: absolute path to the interpreter, absolute path to the entry file, nothing written to stdout, and a full restart after each edit. Check the server in the Inspector first, and the rest is config.</p>
<p>If you want a project where all of this is already in the README, generate one with your own tools at <a href="https://mcp-server-generator-pearl.vercel.app/">MCP Server Generator</a>, or start from the <a href="https://github.com/wuzzheng/mcp-server-template">template repo on GitHub</a>.</p>
]]></content:encoded></item></channel></rss>