Once you have verified your server logic using the MCP Inspector, the next step is to integrate it with a production-grade client. For local development and daily use, Claude Desktop serves as the primary Host application. It manages the lifecycle of your MCP servers, spawning them as subprocesses and communicating via standard input and output (stdio).
To register a server, you must modify a specific configuration file located on your host machine. This JSON file acts as a registry, telling the client which executable to run, what arguments to pass, and which environment variables to inject into the server's context.
The configuration file is named claude_desktop_config.json. Its location depends on your operating system. You may need to create this file and the directory structure if they do not already exist.
~/Library/Application Support/Claude/claude_desktop_config.json%APPDATA%\Claude\claude_desktop_config.jsonYou can quickly access this file through the Claude Desktop interface by selecting the settings menu (often represented by initials or a generic icon) and choosing Settings > Developer > Edit Config. This action opens the file in your default text editor or file explorer.
The core of the configuration is the mcpServers object. Each property within this object represents a distinct server connection. The property name you choose serves as the identifier for that server within the Claude interface.
The value associated with each server is an object containing a command, an array of args, and an optional env dictionary.
Here is the structure for a basic Python-based server configuration:
{
"mcpServers": {
"my-local-data": {
"command": "python3",
"args": [
"/Users/username/projects/mcp-server/main.py"
]
}
}
}
When Claude Desktop initializes, it parses this file and attempts to execute python3 /Users/username/projects/mcp-server/main.py. The client then listens to the stdout of that process for JSON-RPC messages.
Configuration file parsing and process spawning flow in Claude Desktop.
A common failure point in configuration is the use of relative paths. The working directory for the spawned process is often the application bundle location, not your project folder. Therefore, you must always use absolute paths for both the executable and the script.
If you are developing with Python, it is best practice to use the python executable located within your virtual environment. This ensures the server has access to installed dependencies like mcp or pydantic.
For a project using uv or venv located in .venv, the configuration should point directly to the binary:
{
"mcpServers": {
"sqlite-explorer": {
"command": "/Users/username/projects/sqlite-tool/.venv/bin/python",
"args": [
"/Users/username/projects/sqlite-tool/server.py",
"--db-path",
"/Users/username/data/Chinook.db"
]
}
}
}
For TypeScript servers distributed via npm or run with npx, the command requires the full path to the node executable or a shell wrapper that can resolve the command. However, simpler execution is often achieved by using the absolute path to node and the build output:
{
"mcpServers": {
"filesystem-tool": {
"command": "node",
"args": [
"/Users/username/projects/fs-mcp/dist/index.js",
"/Users/username/allowed-directory"
]
}
}
}
Servers often require authentication credentials, such as API keys for external services (e.g., GitHub, Brave Search, or a cloud database). While you could hardcode these into your source code, doing so is a security risk. The MCP configuration allows you to pass these safely via the env object.
Variables defined here are injected into the process environment at runtime. They are accessible via os.environ in Python or process.env in Node.js.
{
"mcpServers": {
"github-integration": {
"command": "uvx",
"args": [
"mcp-server-github"
],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxxxxxxxxxxx",
"LOG_LEVEL": "DEBUG"
}
}
}
}
In this example, we use uvx (part of the uv package manager) to download and run a remote server package instantly, providing the necessary token to authorize requests.
Claude Desktop reads the configuration file only during startup. Any changes made to the JSON file require you to fully quit and restart the application. Closing the window is often insufficient; you must terminate the application process.
Upon restart, look for a connection indicator, typically an icon resembling a plug or a server rack, within the input interface. Clicking this icon reveals the status of your connected servers.
print() for debugging purposes, these messages interfere with the JSON-RPC message structure. Ensure all debugging information is written to standard error (stderr) instead.To diagnose a server that refuses to connect, you can manually run the command and args exactly as defined in your config file from your own terminal.
# Test the command manually to see if it crashes
/Users/username/projects/sqlite-tool/.venv/bin/python /Users/username/projects/sqlite-tool/server.py
If the command runs and hangs (waiting for input) without throwing an exception, the environment setup is likely correct. You can then terminate it and trust that Claude Desktop will be able to spawn it successfully.
Was this section helpful?
subprocess - Subprocess management, Python Software Foundation, 2024 Python Documentation - Official Python library documentation detailing how to interact with operating system processes, covering execution, standard I/O management, and environment variable configuration for spawned child processes.venv - Creation of virtual environments, Python Software Foundation, 2024 Python Documentation - Official Python documentation providing instructions for creating and using virtual environments to isolate project dependencies, which helps maintain clean and reproducible server configurations.© 2026 ApX Machine LearningEngineered with