25 min read
Intermediate
Integration Guides

Integrating with Claude Desktop

Complete guide to connecting your MCP servers with Claude Desktop

MCPgee Team

MCP Expert

Claude Desktop installedCompleted 'Setting Up Your First MCP Server' tutorialBasic understanding of JSON configurationNode.js or Python environment set up

Integrating with Claude Desktop

Introduction

Claude Desktop is Anthropic's native application that provides a powerful interface for interacting with Claude. One of its most compelling features is native support for the Model Context Protocol (MCP), allowing you to extend Claude's capabilities with custom tools and data sources. Claude Code is the CLI counterpart, ideal for developers who prefer terminal-based workflows. Both clients support MCP, and this guide covers configuration for each.

If you have not built a server yet, start with the first MCP server tutorial.

Understanding Claude Desktop's MCP Architecture

How Claude Desktop Uses MCP

Claude Desktop acts as an MCP client, connecting to multiple MCP servers simultaneously via stdio transport. Each server runs as a child process managed by Claude Desktop:

plaintext
┌─────────────────┐     stdio      ┌─────────────────┐
│ Claude Desktop  │ ←────────────→ │   MCP Server 1  │
│   (MCP Client)  │                │  (Your Tools)   │
│                 │     stdio      ├─────────────────┤
│                 │ ←────────────→ │   MCP Server 2  │
│                 │                │ (More Tools)    │
└─────────────────┘                └─────────────────┘

This architecture enables:

  1. Local Processing: Sensitive data never leaves your machine
  2. Custom Tools: Add specialized capabilities to Claude
  3. Resource Access: Give Claude read access to files, databases, and APIs
  4. Process Isolation: Each server runs in its own process with controlled permissions

Setting Up Claude Desktop

Locating the Configuration File

Claude Desktop stores its MCP configuration in a platform-specific location:

macOS:
bash
~/Library/Application Support/Claude/claude_desktop_config.json
Windows:
powershell
%APPDATA%\Claude\claude_desktop_config.json
Linux:
bash
~/.config/Claude/claude_desktop_config.json

Creating the Configuration File

If the file does not exist, create the directory and file:

bash
# macOS
mkdir -p ~/Library/Application\ Support/Claude
echo '{"mcpServers":{}}' > ~/Library/Application\ Support/Claude/claude_desktop_config.json

# Linux
mkdir -p ~/.config/Claude
echo '{"mcpServers":{}}' > ~/.config/Claude/claude_desktop_config.json

On Windows (PowerShell):

powershell
New-Item -ItemType Directory -Force -Path "$env:APPDATA\Claude"
'{"mcpServers":{}}' | Set-Content "$env:APPDATA\Claude\claude_desktop_config.json"

Configuration Deep Dive

Basic Configuration Structure

json
{
  "mcpServers": {
    "server-name": {
      "command": "executable",
      "args": ["arg1", "arg2"],
      "env": {
        "KEY": "value"
      }
    }
  }
}

Configuration Fields Explained

FieldDescriptionTips
Server nameUnique identifier for the serverUse descriptive names like notes-manager
commandThe executable to runUse absolute paths for reliability
argsCommand-line argumentsInclude script paths and flags
envEnvironment variablesUseful for API keys and config

TypeScript Server Configuration

json
{
  "mcpServers": {
    "my-server": {
      "command": "npx",
      "args": ["tsx", "/Users/you/my-server/src/index.ts"],
      "env": {
        "NODE_ENV": "production",
        "LOG_LEVEL": "info"
      }
    }
  }
}

If your server is compiled to JavaScript:

json
{
  "mcpServers": {
    "my-server": {
      "command": "node",
      "args": ["/Users/you/my-server/dist/index.js"]
    }
  }
}

Python Server Configuration

Use the absolute path to the Python binary inside your virtual environment:

json
{
  "mcpServers": {
    "data-analysis": {
      "command": "/Users/you/my-server/venv/bin/python",
      "args": ["/Users/you/my-server/server.py"],
      "env": {
        "PYTHONUNBUFFERED": "1",
        "DATA_DIR": "/Users/you/datasets"
      }
    }
  }
}
Important: Always set PYTHONUNBUFFERED=1 for Python MCP servers. Without it, Python buffers stdout and the MCP protocol messages may not reach Claude Desktop in time.

Using npx for Published Servers

Many published MCP servers can be run directly with npx:

json
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "/Users/you/Documents"
      ]
    },
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_TOKEN": "${GITHUB_TOKEN}"
      }
    }
  }
}

Browse available servers at the servers directory.

Multi-Server Configuration

Claude Desktop supports connecting to multiple servers simultaneously. Each server provides its own tools and resources:

json
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/you/projects"]
    },
    "database": {
      "command": "node",
      "args": ["/Users/you/mcp-servers/postgres/dist/index.js"],
      "env": {
        "DATABASE_URL": "${DATABASE_URL}"
      }
    },
    "notes": {
      "command": "npx",
      "args": ["tsx", "/Users/you/mcp-servers/notes/src/index.ts"]
    }
  }
}

Claude will aggregate tools from all servers and present them in a unified interface.

Setting Up Claude Code

Claude Code is Anthropic's CLI for using Claude in the terminal. It supports MCP servers through a command-line interface.

Adding Servers

bash
# Add a server using npx
claude mcp add filesystem npx @modelcontextprotocol/server-filesystem /Users/you/Documents

# Add a custom server
claude mcp add my-notes npx tsx /Users/you/my-server/src/index.ts

# Add with environment variables
claude mcp add github npx @modelcontextprotocol/server-github -e GITHUB_TOKEN=ghp_xxx

Listing and Managing Servers

bash
# List configured servers
claude mcp list

# Remove a server
claude mcp remove my-notes

Scoped Configuration

Claude Code supports project-level and user-level MCP configuration:

  • Project scope (.claude/settings.json in the project root): Servers available only in this project
  • User scope (~/.claude/settings.json): Servers available globally

Building Integration-Ready Servers

Server Requirements

For a server to work with Claude Desktop and Claude Code:

  1. Use stdio transport: Communicate via standard input/output
  2. Log to stderr: stdout is reserved for protocol messages
  3. Handle initialization: Respond to the initialize handshake
  4. Graceful shutdown: Handle SIGINT and SIGTERM

TypeScript Template (McpServer)

typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';

const server = new McpServer({
  name: 'my-claude-server',
  version: '1.0.0',
});

// Register tools with rich descriptions for Claude
server.tool(
  'analyzeCode',
  'Analyze source code for potential improvements, bugs, and security issues',
  {
    code: z.string().describe('The source code to analyze'),
    language: z.enum(['javascript', 'typescript', 'python', 'java']).describe('Programming language'),
    focus: z.array(z.enum(['performance', 'security', 'readability', 'bugs'])).optional(),
  },
  async ({ code, language, focus }) => {
    const analysis = await performAnalysis(code, language, focus);
    return {
      content: [{ type: 'text', text: formatAnalysis(analysis) }],
    };
  }
);

// All logging goes to stderr
console.error('Starting server...');

const transport = new StdioServerTransport();
await server.connect(transport);
console.error('Server connected to Claude');

Python Template (FastMCP)

python
import sys
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("my-claude-server")

@mcp.tool()
def analyze_code(code: str, language: str) -> str:
    """Analyze source code for improvements and bugs.

    Args:
        code: The source code to analyze
        language: Programming language (javascript, python, etc.)
    """
    # All logging to stderr
    print("Analyzing code...", file=sys.stderr)
    analysis = perform_analysis(code, language)
    return format_analysis(analysis)

if __name__ == "__main__":
    mcp.run()

Writing Good Tool Descriptions

Claude uses your tool descriptions to decide when to invoke them. Write descriptions that:

  • Explain what the tool does in one sentence
  • Mention when it should be used
  • Describe each parameter clearly

typescript
// Good: clear, specific, actionable
server.tool('searchFiles', {
  description: 'Search for files by name or content pattern within the project directory. Use this when the user asks to find or locate specific files.',
  inputSchema: {
    pattern: z.string().describe('Glob pattern or text to search for'),
    directory: z.string().optional().describe('Subdirectory to search in, defaults to project root'),
  },
}, handler);

// Bad: vague, no context
server.tool('search', {
  description: 'Searches for things',
  inputSchema: { q: z.string() },
}, handler);

Testing and Debugging

1. Test Your Server Standalone

Before connecting to Claude, verify your server works:

bash
# Send an initialize request
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}' | npx tsx src/index.ts

2. Validate Your Configuration

bash
# Check JSON syntax
python3 -m json.tool < ~/Library/Application\ Support/Claude/claude_desktop_config.json

3. Check Claude Desktop Logs

If a server fails to connect, check the Claude Desktop logs:

  • macOS: ~/Library/Logs/Claude/
  • Windows: %LOCALAPPDATA%\Claude\logs
  • Linux: ~/.local/share/Claude/logs/

4. Common Debugging Steps

  1. Run the exact command from your config in a terminal
  2. Check that all paths are absolute (not relative)
  3. Verify the executable exists and is accessible
  4. Ensure environment variables are set correctly
  5. Confirm stdout has only JSON-RPC messages, not debug output

Troubleshooting Common Issues

Server Not Appearing in Claude

Checklist:
  • [ ] JSON syntax is valid (no trailing commas, proper quotes)
  • [ ] All paths are absolute
  • [ ] The command executable exists and is in PATH
  • [ ] Claude Desktop was fully restarted (Cmd+Q / Alt+F4, not just window close)
  • [ ] Config file is in the correct platform-specific location

Server Disconnects Immediately

Common causes:

  • Server crashes during initialization (check stderr output)
  • Missing dependencies (run npm install or pip install)
  • Wrong Node.js or Python version
  • The server writes non-protocol output to stdout

Environment Variables Not Working

  • Use ${VAR_NAME} syntax to reference system environment variables
  • On macOS, GUI apps may not inherit terminal environment - set variables system-wide or in the env config field
  • For secrets, consider using a .env file loaded by your server

Real-World Configuration Examples

Development Environment

json
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/dev/projects"]
    },
    "git": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" }
    },
    "database": {
      "command": "npx",
      "args": ["tsx", "/Users/dev/mcp-servers/postgres/src/index.ts"],
      "env": { "DATABASE_URL": "postgresql://localhost/devdb" }
    }
  }
}

Research and Knowledge Management

json
{
  "mcpServers": {
    "papers": {
      "command": "python3",
      "args": ["/Users/researcher/mcp-servers/arxiv/server.py"],
      "env": { "CACHE_DIR": "/Users/researcher/.cache/arxiv" }
    },
    "notes": {
      "command": "npx",
      "args": ["tsx", "/Users/researcher/mcp-servers/obsidian/src/index.ts"],
      "env": { "VAULT_PATH": "/Users/researcher/Documents/Obsidian" }
    }
  }
}

Best Practices

1. Security

  • Never hardcode API keys in configuration - use environment variables
  • Set file permissions on claude_desktop_config.json to restrict access
  • Limit what directories and data your servers can access
  • Use the principle of least privilege when designing tools

2. Performance

  • Keep server startup fast by lazy-loading heavy dependencies
  • Implement caching for expensive operations
  • Log to stderr only when needed to reduce I/O

3. User Experience

  • Write clear, specific tool descriptions so Claude knows when to use each tool
  • Format tool output as structured text that Claude can reason about
  • Return helpful error messages, not stack traces

4. Reliability

  • Add error handling for all tool operations
  • Handle SIGINT/SIGTERM for graceful shutdown
  • Test your server with various inputs before deploying

Conclusion

Integrating MCP servers with Claude Desktop and Claude Code opens up powerful possibilities for extending Claude with your own tools and data sources. The configuration is straightforward, and both clients support connecting to multiple servers simultaneously.

Key takeaways:

  • Use claude_desktop_config.json for Claude Desktop and claude mcp add for Claude Code
  • All paths must be absolute and all logging must go to stderr
  • Write clear tool descriptions to help Claude use your tools effectively
  • Test your server standalone before connecting to Claude

Ready to build more advanced servers? Continue to the Build MCP Server tutorial for production-ready patterns, or explore the servers directory for existing servers you can use immediately.

Code Examples

Complete Claude Desktop Configurationjson
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/you/Documents"],
      "env": {}
    },
    "notes": {
      "command": "npx",
      "args": ["tsx", "/Users/you/mcp-servers/notes/src/index.ts"],
      "env": {
        "NODE_ENV": "production",
        "NOTES_DIR": "/Users/you/Documents/notes"
      }
    },
    "database": {
      "command": "/Users/you/mcp-servers/pg/venv/bin/python",
      "args": ["/Users/you/mcp-servers/pg/server.py"],
      "env": {
        "PYTHONUNBUFFERED": "1",
        "DATABASE_URL": "${DATABASE_URL}"
      }
    },
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_TOKEN": "${GITHUB_TOKEN}"
      }
    }
  }
}
Production McpServer for Claude Desktoptypescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';

const server = new McpServer({
  name: 'production-server',
  version: '1.0.0',
});

// Tool with rich description for Claude
server.tool(
  'searchKnowledgeBase',
  'Search through your knowledge base using natural language. Use this when the user asks to find information, look up notes, or search documents.',
  {
    query: z.string().min(1).max(500).describe('Natural language search query'),
    limit: z.number().min(1).max(50).default(10).describe('Max results to return'),
    tags: z.array(z.string()).optional().describe('Filter by tags'),
  },
  async ({ query, limit, tags }) => {
    try {
      const results = await searchIndex(query, { limit, tags });
      return {
        content: [{
          type: 'text',
          text: results.length
            ? results.map(r => `## ${r.title}\n${r.excerpt}`).join('\n\n')
            : 'No results found for your query.',
        }],
      };
    } catch (error) {
      console.error('Search failed:', error);
      return {
        content: [{ type: 'text', text: 'Search temporarily unavailable. Please try again.' }],
      };
    }
  }
);

// Graceful shutdown
process.on('SIGINT', () => process.exit(0));
process.on('SIGTERM', () => process.exit(0));

const transport = new StdioServerTransport();
await server.connect(transport);
console.error('Server ready');
FastMCP Server for Claudepython
import sys
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("knowledge-base")

@mcp.tool()
def search_knowledge_base(query: str, limit: int = 10) -> str:
    """Search through your knowledge base using natural language.

    Use this when the user asks to find information, look up notes,
    or search documents.

    Args:
        query: Natural language search query
        limit: Maximum number of results to return (default 10)
    """
    print(f"Searching for: {query}", file=sys.stderr)
    results = search_index(query, limit=limit)
    if not results:
        return "No results found for your query."
    return "\n\n".join(
        f"## {r['title']}\n{r['excerpt']}" for r in results
    )

@mcp.resource("kb://stats")
def knowledge_base_stats() -> str:
    """Return knowledge base statistics."""
    import json
    stats = get_kb_stats()
    return json.dumps(stats)

if __name__ == "__main__":
    mcp.run()

Key Takeaways

  • Claude Desktop uses claude_desktop_config.json with platform-specific paths for MCP configuration
  • Claude Code uses the claude mcp add command for quick server setup from the terminal
  • All MCP servers must use stdio transport and log to stderr, never stdout
  • Multiple servers can run simultaneously, each providing different tools and resources
  • Clear tool descriptions are critical for Claude to know when and how to use your tools

Troubleshooting

Server does not appear in Claude Desktop after configuration

Verify JSON syntax is valid (no trailing commas). Ensure all paths are absolute. Fully quit Claude Desktop (Cmd+Q on macOS, not just close window) and relaunch. Check that the command executable exists by running it manually in a terminal.

Server starts but immediately disconnects

Check that your server does not write debug output to stdout - only JSON-RPC protocol messages should go to stdout. All logging must use stderr. Test by running the server manually and sending an initialize request via echo piped to the process.

Environment variables not working in configuration

Use the ${VAR_NAME} syntax to reference system environment variables. On macOS, GUI apps do not inherit terminal environment variables, so either set them system-wide in /etc/launchd.conf or pass them directly in the env config field.

Tools work in testing but fail in Claude Desktop

Verify that your inputSchema matches what you actually accept. Check that all required fields are marked correctly. Ensure error responses follow the MCP format. Test with edge cases like empty strings and missing optional fields.

Claude does not use my tools even though they are connected

Improve your tool descriptions. Claude decides which tools to use based on the description field. Make descriptions specific and action-oriented. Mention the types of user requests the tool should handle.

Next Steps

  • Build production-ready servers with the Build MCP Server tutorial
  • Browse the servers directory for pre-built servers to add to Claude
  • Explore building a Python MCP server for data-oriented workflows
  • Learn about MCP security fundamentals for enterprise deployments

Was this helpful?

Share tutorial:

Stay Updated with MCP Insights

Join 5,000+ developers and get weekly insights on MCP development, new server releases, and implementation strategies delivered to your inbox.

We respect your privacy. Unsubscribe at any time.

MCPgee Team

We write in-depth guides, tutorials, and reviews to help developers get the most out of the Model Context Protocol ecosystem.

Frequently Asked Questions

Explore MCP Servers

Browse our directory of 33,000+ MCP servers. Find the perfect tools for your AI-powered workflows.