Advanced MCP Integration
Connect Claude Code to external tools, databases, and custom functionality using Model Context Protocol.
What you'll accomplish
Set up and use MCP servers to extend Claude Code's capabilities.
Prerequisites
- Claude Code working environment
- Understanding of APIs and JSON
- Node.js for custom servers
What is MCP?
MCP (Model Context Protocol) is a standard for connecting AI assistants to external tools and data sources. It allows Claude Code to:
- Query databases directly
- Access APIs and external services
- Use specialized tools you create
- Integrate with existing systems
Your browser can have extensions that add features (ad blockers, password managers). MCP works similarly for Claude Code:
- Without MCP: Claude can only read/write files and run commands
- With MCP: Claude can access databases, APIs, and custom tools
You "install" MCP servers that give Claude new abilities.
MCP Architecture
┌─────────────────┐
│ Claude Code │
│ (Client) │
└────────┬────────┘
│ MCP Protocol (JSON-RPC)
▼
┌─────────────────┐ ┌─────────────────┐
│ MCP Server 1 │ │ MCP Server 2 │
│ (Database) │ │ (Custom API) │
└─────────────────┘ └─────────────────┘
│ │
▼ ▼
PostgreSQL Your API
Client: Claude Code requests data/actions Servers: Programs that fulfill those requests
Configuring MCP Servers
Settings Location
MCP servers are configured in your settings:
json// ~/.claude/settings.json or .claude/settings.json { "mcpServers": { "database": { "command": "npx", "args": ["@modelcontextprotocol/server-postgres"], "env": { "DATABASE_URL": "postgresql://user:pass@localhost/db" } }, "filesystem": { "command": "npx", "args": ["@modelcontextprotocol/server-filesystem", "/data"] } } }
Server Configuration Options
| Option | Description | Required |
|---|---|---|
command | Executable to run | Yes |
args | Command arguments | No |
env | Environment variables | No |
cwd | Working directory | No |
Built-in MCP Servers
Anthropic provides several pre-built MCP servers:
Filesystem Server
Access files beyond the project directory:
json{ "mcpServers": { "docs": { "command": "npx", "args": ["@modelcontextprotocol/server-filesystem", "/path/to/docs"] } } }
Now Claude can access: Read the file at /path/to/docs/readme.md
PostgreSQL Server
Query PostgreSQL databases:
json{ "mcpServers": { "postgres": { "command": "npx", "args": ["@modelcontextprotocol/server-postgres"], "env": { "POSTGRES_HOST": "localhost", "POSTGRES_PORT": "5432", "POSTGRES_USER": "myuser", "POSTGRES_PASSWORD": "mypass", "POSTGRES_DATABASE": "mydb" } } } }
Now Claude can: Query the users table to find inactive accounts
SQLite Server
For local SQLite databases:
json{ "mcpServers": { "sqlite": { "command": "npx", "args": ["@modelcontextprotocol/server-sqlite", "--db", "/path/to/database.db"] } } }
GitHub Server
Access GitHub repositories and issues:
json{ "mcpServers": { "github": { "command": "npx", "args": ["@modelcontextprotocol/server-github"], "env": { "GITHUB_TOKEN": "ghp_xxxxxxxxxxxx" } } } }
Now Claude can: List open issues in the anthropics/claude-code repo
Slack Server
Read and send Slack messages:
json{ "mcpServers": { "slack": { "command": "npx", "args": ["@modelcontextprotocol/server-slack"], "env": { "SLACK_TOKEN": "xoxb-xxxxx" } } } }
Creating Custom MCP Servers
You can build your own MCP servers for custom functionality.
Basic Structure (TypeScript)
typescript// custom-server.ts import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; const server = new Server({ name: "my-custom-server", version: "1.0.0", }); // Define available tools server.setRequestHandler("tools/list", async () => ({ tools: [ { name: "get_weather", description: "Get current weather for a location", inputSchema: { type: "object", properties: { location: { type: "string", description: "City name or coordinates", }, }, required: ["location"], }, }, ], })); // Handle tool calls server.setRequestHandler("tools/call", async (request) => { if (request.params.name === "get_weather") { const location = request.params.arguments.location; // Your implementation here const weather = await fetchWeather(location); return { content: [{ type: "text", text: JSON.stringify(weather) }], }; } throw new Error("Unknown tool"); }); // Start the server const transport = new StdioServerTransport(); await server.connect(transport);
Package Setup
json// package.json { "name": "my-mcp-server", "version": "1.0.0", "type": "module", "bin": { "my-mcp-server": "./dist/server.js" }, "dependencies": { "@modelcontextprotocol/sdk": "^0.5.0" } }
Using Your Server
After building (npm run build):
json{ "mcpServers": { "weather": { "command": "node", "args": ["/path/to/my-mcp-server/dist/server.js"] } } }
Advanced Examples
Database + AI Queries
json{ "mcpServers": { "business-data": { "command": "node", "args": ["./mcp/business-server.js"], "env": { "DB_URL": "postgresql://...", "ALLOW_WRITES": "false" } } } }
Now you can ask Claude: What were our top 10 selling products last month?
Claude will:
- Use the MCP server to query the database
- Process the results
- Present them in a useful format
Multi-Service Integration
Connect multiple services:
json{ "mcpServers": { "database": { "command": "npx", "args": ["@modelcontextprotocol/server-postgres"], "env": { "DATABASE_URL": "..." } }, "analytics": { "command": "node", "args": ["./mcp/analytics-server.js"], "env": { "ANALYTICS_KEY": "..." } }, "notifications": { "command": "node", "args": ["./mcp/slack-notifier.js"], "env": { "SLACK_WEBHOOK": "..." } } } }
Now Claude can: Check if any products are running low, and if so, notify the #inventory Slack channel
Read-Only Access
For safety, many MCP servers support read-only mode:
json{ "mcpServers": { "prod-db": { "command": "npx", "args": ["@modelcontextprotocol/server-postgres", "--read-only"], "env": { "DATABASE_URL": "postgresql://readonly_user:pass@prod-server/db" } } } }
Warning
Production databases: Always use read-only credentials when connecting Claude to production data. Create a dedicated read-only database user.
MCP Tool Types
MCP servers can provide:
Tools
Functions Claude can call:
typescript{ tools: [ { name: "create_ticket", description: "Create a support ticket", inputSchema: { type: "object", properties: { title: { type: "string" }, priority: { type: "string", enum: ["low", "medium", "high"] } } } } ] }
Resources
Data Claude can read:
typescript{ resources: [ { uri: "config://app-settings", name: "Application Settings", mimeType: "application/json" } ] }
Prompts
Reusable prompt templates:
typescript{ prompts: [ { name: "code-review", description: "Standard code review template", arguments: [ { name: "language", required: true } ] } ] }
Debugging MCP Servers
Enable Verbose Logging
bashclaude --verbose
This shows MCP communication details.
Test Server Manually
Run your server directly and send test requests:
bashecho '{"jsonrpc":"2.0","method":"tools/list","id":1}' | node ./my-server.js
Check Server Logs
Add logging to your server:
typescriptimport { createWriteStream } from 'fs'; const log = createWriteStream('/tmp/mcp-server.log', { flags: 'a' }); function debug(msg: string) { log.write(`[${new Date().toISOString()}] ${msg}\n`); } debug('Server starting...');
Common Issues
Frequently Asked Questions
Check:
- Command path is correct (use absolute paths)
- Dependencies are installed (
npm install) - Server starts without errors when run manually
- Settings JSON is valid
- Restart Claude Code after settings changes
- Check
tools/listreturns your tool - Verify tool name matches exactly
- Check for errors in server output
- Verify database credentials
- Check network connectivity from server
- Ensure user has SELECT permissions
- Test query manually first
- Use
host.docker.internalinstead oflocalhost - Ensure network is configured correctly
- Mount MCP server files into container
- Pass environment variables to container
Security Best Practices
1. Use Read-Only Where Possible
json{ "env": { "MODE": "readonly", "ALLOW_WRITES": "false" } }
2. Limit Data Access
Create database views instead of direct table access:
sqlCREATE VIEW safe_users AS SELECT id, username, created_at FROM users; -- Excludes sensitive columns like password_hash, email
3. Rate Limiting
Implement rate limiting in your server:
typescriptconst rateLimit = new Map<string, number>(); function checkRateLimit(tool: string): boolean { const count = rateLimit.get(tool) || 0; if (count > 100) return false; rateLimit.set(tool, count + 1); return true; }
4. Audit Logging
Log all MCP operations:
typescriptfunction logOperation(tool: string, args: unknown) { console.error(JSON.stringify({ timestamp: new Date().toISOString(), tool, args, })); }
5. Separate Credentials
Use different credentials for different environments:
json{ "mcpServers": { "dev-db": { "env": { "DATABASE_URL": "${DEV_DATABASE_URL}" } }, "prod-db": { "env": { "DATABASE_URL": "${PROD_DATABASE_URL}" } } } }
Verify Your Understanding
You've completed this lesson if you can:
Next: Security best practices for Claude Code in professional environments.