Complete guide to Model Context Protocol server configuration and usage.
From claude-code-expertnpx claudepluginhub markus41/claude --plugin claude-code-expertThis skill uses the workspace's default tool permissions.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
Complete guide to Model Context Protocol server configuration and usage.
MCP (Model Context Protocol) allows Claude Code to connect to external servers that provide additional tools, resources, and capabilities. Supports 300+ external tools and services.
| Transport | Description | Recommended |
|---|---|---|
http | HTTP-based (streamable) | Yes (recommended) |
sse | Server-Sent Events | Deprecated |
stdio | Local process via stdin/stdout | For local servers |
# HTTP server (recommended)
claude mcp add --transport http github https://api.githubcopilot.com/mcp/
# SSE server (deprecated)
claude mcp add --transport sse asana https://mcp.asana.com/sse
# Local stdio server
claude mcp add --transport stdio my-db -- npx -y @some/package
# With environment variables
claude mcp add --transport stdio -e AIRTABLE_API_KEY=YOUR_KEY airtable -- npx -y airtable-mcp-server
# With scope
claude mcp add --scope project server-name -- command args
# List configured servers
claude mcp list
# Get server details
claude mcp get server-name
# Remove server
claude mcp remove server-name
| Scope | Storage | Shared |
|---|---|---|
local (default) | ~/.claude.json | No (personal, this project) |
project | .mcp.json | Yes (version controlled) |
user | ~/.claude.json with scope flag | No (personal, all projects) |
MCP servers are configured in .mcp.json at the project root.
{
"mcpServers": {
"server-name": {
"type": "stdio",
"command": "executable",
"args": ["arg1", "arg2"],
"env": {
"KEY": "value"
},
"disabled": false
}
}
}
{
"mcpServers": {
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/"
}
}
}
{
"mcpServers": {
"my-server": {
"command": "${CLAUDE_PLUGIN_ROOT}/servers/api",
"args": ["--config", "${CLAUDE_PLUGIN_ROOT}/config.json"],
"env": {
"API_KEY": "${MY_API_KEY}",
"PORT": "${PORT:-3000}"
}
}
}
}
${VAR} expands to env var value. ${VAR:-default} provides fallback.
~/.claude.json (personal, one project).mcp.json in project root (checked into git)~/.claude.json with user scope (personal, all projects)| Field | Type | Description |
|---|---|---|
type | string | Transport: stdio, http, sse |
command | string | Executable to run (stdio) |
args | string[] | Arguments to pass (stdio) |
url | string | Server URL (http/sse) |
headers | object | HTTP headers (http/sse) |
env | object | Environment variables |
disabled | boolean | Temporarily disable server |
cwd | string | Working directory for the server |
# Add server interactively
claude mcp add
# Add with name and command
claude mcp add server-name -- command arg1 arg2
# Add with scope
claude mcp add --scope project server-name -- npx -y @package/server
# Add with environment variables
claude mcp add server-name -e KEY=value -- command args
# List configured servers
claude mcp list
# Remove server
claude mcp remove server-name
# Get server details
claude mcp get server-name
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/allowed/path"]
}
}
}
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"DATABASE_URL": "postgresql://user:pass@localhost:5432/dbname"
}
}
}
}
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_..."
}
}
}
}
{
"mcpServers": {
"brave-search": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-brave-search"],
"env": {
"BRAVE_API_KEY": "BSA..."
}
}
}
}
{
"mcpServers": {
"puppeteer": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-puppeteer"]
}
}
}
{
"mcpServers": {
"memory": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-memory"]
}
}
}
{
"mcpServers": {
"slack": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-slack"],
"env": {
"SLACK_BOT_TOKEN": "xoxb-...",
"SLACK_TEAM_ID": "T..."
}
}
}
}
{
"mcpServers": {
"sentry": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-sentry"],
"env": {
"SENTRY_AUTH_TOKEN": "sntrys_..."
}
}
}
}
{
"mcpServers": {
"firecrawl": {
"command": "npx",
"args": ["-y", "firecrawl-mcp"],
"env": {
"FIRECRAWL_API_KEY": "fc-..."
}
}
}
}
{
"mcpServers": {
"context7": {
"command": "npx",
"args": ["-y", "@context7/mcp-server"]
}
}
}
{
"mcpServers": {
"perplexity": {
"command": "npx",
"args": ["-y", "perplexity-mcp"],
"env": {
"PERPLEXITY_API_KEY": "pplx-..."
}
}
}
}
Some MCP servers support OAuth authentication:
# Add OAuth-enabled server
claude mcp add --transport http \
--callback-port 8080 \
--client-id "my-client-id" \
--client-secret "my-secret" \
github https://api.githubcopilot.com/mcp/
{
"mcpServers": {
"oauth-server": {
"type": "http",
"url": "https://api.example.com/mcp/",
"oauth": {
"clientId": "your-client-id",
"clientSecret": "your-client-secret",
"callbackPort": 8080,
"scopes": ["read", "write"]
}
}
}
}
# Add MCP server from JSON blob
claude mcp add-json my-server '{"command":"node","args":["server.js"]}'
# Import servers from Claude Desktop app
claude mcp add-from-claude-desktop
# Reset MCP server (clear cached state)
claude mcp reset server-name
MCP tools are exposed to Claude with the naming pattern:
mcp__<server-name>__<tool-name>
For example:
mcp__filesystem__read_filemcp__postgres__querymcp__github__create_issueFor remote MCP servers using Server-Sent Events:
{
"mcpServers": {
"remote-server": {
"url": "https://my-server.example.com/mcp/sse",
"headers": {
"Authorization": "Bearer token123"
}
}
}
}
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
ListToolsRequestSchema,
CallToolRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
const server = new Server(
{ name: "my-server", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "my_tool",
description: "Does something useful",
inputSchema: {
type: "object",
properties: {
query: { type: "string", description: "The query" }
},
required: ["query"]
}
}
]
}));
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "my_tool") {
const { query } = request.params.arguments;
return {
content: [{ type: "text", text: `Result for: ${query}` }]
};
}
throw new Error(`Unknown tool: ${request.params.name}`);
});
// Start server
const transport = new StdioServerTransport();
await server.connect(transport);
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
server = Server("my-server")
@server.list_tools()
async def list_tools():
return [
Tool(
name="my_tool",
description="Does something useful",
inputSchema={
"type": "object",
"properties": {
"query": {"type": "string"}
},
"required": ["query"]
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict):
if name == "my_tool":
return [TextContent(type="text", text=f"Result: {arguments['query']}")]
raise ValueError(f"Unknown tool: {name}")
async def main():
async with stdio_server() as (read, write):
await server.run(read, write)
import asyncio
asyncio.run(main())
# Check server status
claude mcp list
# Test server manually
echo '{"jsonrpc":"2.0","method":"initialize","params":{"capabilities":{}},"id":1}' | npx -y @package/server
# Check Claude Code logs
claude --verbose
env fieldnpx -y to auto-accept package installcommand when needed.mcp.json should be in .gitignore or use env var references@modelcontextprotocol/server-* packagesMost engineers only use MCP Tools and miss this entirely. MCP Prompts are the highest-leverage primitive in the MCP spec.
MCP has three primitives: Tools (Claude invokes), Resources (data sources), and Prompts (pre-built conversation starters). Prompts are server-defined templates that prime Claude with everything needed for complex workflows — available via slash commands in the UI or programmatic invocation.
A Prompt is a structured message template that a server exposes. When invoked, it injects a complete, expert-quality system message into the conversation. Unlike Tools (which Claude calls to get data), Prompts set up how Claude should think and act for the entire workflow.
Prompts vs Tools:
| Prompts | Tools | |
|---|---|---|
| When invoked | At conversation start or via slash command | During conversation as needed |
| What they provide | Structured messages that prime the agent | Data or action results |
| Best for | Workflow setup, expertise injection, complex multi-step tasks | Data retrieval, actions, side effects |
| Visibility | Appear as /server:prompt-name in UI | Called by Claude internally |
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import {
ListPromptsRequestSchema,
GetPromptRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
const server = new Server(
{ name: "my-server", version: "1.0.0" },
{ capabilities: { prompts: {}, tools: {} } } // declare prompts capability
);
// List available prompts
server.setRequestHandler(ListPromptsRequestSchema, async () => ({
prompts: [
{
name: "deploy-checklist",
description: "Run full pre-deployment verification checklist",
arguments: [
{
name: "environment",
description: "Target environment (staging/production)",
required: true
}
]
},
{
name: "security-review",
description: "Deep security audit of recent changes",
arguments: []
}
]
}));
// Return prompt messages when invoked
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "deploy-checklist") {
const env = args?.environment ?? "staging";
return {
messages: [
{
role: "user",
content: {
type: "text",
text: `You are a deployment safety engineer. Run a complete pre-deployment checklist for the ${env} environment:
1. Check all tests pass
2. Verify no .env files are staged
3. Confirm version bump in package.json
4. Review last 3 commits for accidental secrets
5. Check docker image tags are not :latest
6. Verify rollback plan is documented
Report each item as PASS/FAIL/WARN with a one-line reason.`
}
}
]
};
}
if (name === "security-review") {
return {
messages: [
{
role: "user",
content: {
type: "text",
text: `You are a security engineer. Review the recent git diff for:
- Hardcoded credentials or API keys
- SQL injection vulnerabilities
- XSS attack surfaces
- Insecure deserialization
- Path traversal vulnerabilities
- Missing auth checks on new endpoints
Run git diff HEAD~3 and analyze every changed file.`
}
}
]
};
}
throw new Error(`Unknown prompt: ${name}`);
});
Instead of loading 500 lines of deployment instructions into every session, expose a deploy-checklist prompt. It loads zero tokens at session start and activates only when needed.
Pattern: One Prompt = one expert persona + one workflow. Keep Prompts focused.
| Prompt Name | What It Primes |
|---|---|
deploy-checklist | Pre-deployment verification workflow |
security-audit | OWASP-aware code review persona |
architecture-review | System design review with ADR output |
incident-response | On-call SRE persona for outage triage |
db-migration-review | Database change safety checklist |
api-review | REST/GraphQL API design reviewer |
pr-summary | Auto-generate PR descriptions from diff |
test-strategy | Test coverage analysis and planning |
Prompts inject messages only when invoked. A Prompt with 500 tokens of expert instructions costs nothing until used, vs CLAUDE.md where those tokens load every session.
Rough math: 10 Prompts × 500 tokens each = 5,000 tokens available on demand vs 5,000 tokens consumed per session if put in CLAUDE.md.