Key Takeaways
- Learn why MCP servers must use file-relative .env paths.
- Claude Desktop's config lacks cwd, so relative paths break.
- Fix:
os.path.join(os.path.dirname(os.path.abspath(__file__)), '.env').
The Problem: MCP Servers and Unpredictable Working Directories

When you build an MCP server and configure it in Claude Desktop or another client, you typically specify command and args — but rarely cwd. The client launches your server as a subprocess, and that subprocess inherits the client's working directory, not the directory where your script lives.
This is a silent trap. If your server loads a .env file with a relative path like ".env", you're assuming the process's current working directory is the repo root. Nothing in your code enforces that, and nothing in the client config guarantees it.
The Fix: Resolve .env Relative to file
The robust solution is to anchor your .env path to the server script's own location. In Python, that looks like this:
import os
def load_env(path):
try:
f = open(path, encoding="utf-8")
except FileNotFoundError:
return
for line in f:
line = line.strip()
if "=" in line and not line.startswith("#"):
k, v = line.split("=", 1)
os.environ.setdefault(k, v.strip().strip('"').strip("'"))
here = os.path.dirname(os.path.abspath(__file__))
load_env(os.path.join(here, ".env"))
Note the key line: here = os.path.dirname(os.path.abspath(__file__)). This works regardless of where the client launches the process from.
Why This Matters for Claude Code Users
You likely use Claude Code to build and manage MCP servers. If you're developing a custom MCP server that reads API keys from .env, you've probably tested it by running python server.py from your repo root. It worked — so you shipped it. But Claude Desktop (or any MCP client) might launch it from ~ or /Applications or anywhere else. The moment that happens, your server silently fails to load credentials.
The failure is especially insidious: the server starts fine, but every tool that needs an API key returns an auth error. You debug for hours before noticing the env vars are missing.
Try It Now: Audit Your MCP Servers

- Find all your MCP server scripts — look for Python (or Node) files that read
.envorprocess.env. - Check their
.envloading code — does it use a relative path like".env"? If so, fix it. - For Node.js, use
path.join(__dirname, '.env')instead of'.env'. - Test from a different directory:
cd /tmp && python /path/to/your/server.py— does it still load env? If not, you've found the bug.
The Deeper Lesson: Don't Trust Implicit Assumptions
This bug is a symptom of a broader principle: never let your code rely on the environment's current working directory. MCP servers are especially vulnerable because they're launched by external clients you don't control.
Beyond .env, audit your server for other relative path usage — file logging, SQLite databases, temp directories. All of them should be anchored to __file__ or explicitly configured.
Conclusion
One line of code — os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env") — saves you from a whole class of "works on my machine" failures. Apply it to every MCP server you build, and you'll never lose a session to a missing API key again.
Source: dev.to
[Updated 02 Aug via devto_mcp]
A follow-up audit by the same developer uncovered a second instance of the same bug, sitting just nineteen lines below the original fix. The update_article MCP tool writes an audit trail to logs/article_updates.jsonl using a bare relative path, resolved against the process's current working directory at open time. When launched from a different directory, the log silently writes to the wrong location or fails to create the logs/ directory. The author verified the behavior by running the function standalone, confirming it's plain os.path logic, not MCP-specific. This reveals a broader pattern: fixing one relative-path instance doesn't guarantee others aren't lurking nearby. [per dev.to]
[Updated 02 Aug via devto_mcp]
The same developer's follow-up audit reveals that MCP schema drift is not a constant rate but clusters in a small volatile subset. Across three snapshots of 474 servers, only 24 (5.1%) changed contracts by +72h, versus 21 (4.4%) at +36h — just 57% of the linear projection. Notably, zero of the 21 reverted, and 3 changed again, indicating persistent churn among a few actively-developed servers. Additionally, 18% of tools declare an outputSchema, meaning 82% have no formal output contract to break, a hidden risk not captured by drift measurements. [per dev.to]









