Skip to content

How to Make Claude Code Work Better With Skills and MCP Servers

Problem

Claude Code disappointed me at first. I installed it, tried to use it, and got generic, often incorrect suggestions that didn’t match my project. Meanwhile, other developers praised Codex for working well “out of the box.”

The frustration came from specific patterns I kept seeing:

  • Generic suggestions: Code that looked correct but ignored my project’s conventions
  • No memory: Each session started fresh, forgetting everything from previous work
  • Wrong architectural decisions: Changes that violated my team’s standards
  • Limited tool access: No connection to my docs, databases, or services

I almost gave up on Claude Code. Then I discovered the real issue: Claude Code requires explicit configuration with skills, MCP servers, and guardrails to work well. Codex handles this automatically; Claude Code expects you to set it up.

What I Found

Let me show you what changed my perspective.

I compared notes with developers who had positive experiences with Claude Code. The difference was striking:

+------------------------+ +------------------------+ +------------------------+
| Developer Type A | | Developer Type B | | Developer Type C |
| (Disappointed) | | (Satisfied) | | (Power User) |
+------------------------+ +------------------------+ +------------------------+
| Default installation | | Basic configuration | | Full configuration |
| No CLAUDE.md | | Simple CLAUDE.md | | Comprehensive setup |
| No skills | | 1-2 skills | | Multiple skills |
| No MCP servers | | 1 MCP server | | Several MCP servers |
| No guardrails | | Basic guardrails | | Custom guardrails |
+------------------------+ +------------------------+ +------------------------+
| | |
v v v
"Claude is shit" "Claude is okay" "Claude is amazing"

The perception that Claude underperforms isn’t about capability. It’s about configuration.

The Missing Pieces

I identified four critical components that Claude Code lacks by default.

1. Project Context

Without explicit configuration, Claude Code operates on general coding knowledge rather than project-specific intelligence.

Claude's default state (problematic):
- Doesn't know my project's architecture
- Doesn't follow my coding conventions
- Doesn't understand my team's patterns
- Makes generic suggestions that break my codebase

2. Memory Across Sessions

By default, Claude Code doesn’t retain insights from previous sessions. Each conversation starts completely fresh.

Session 1: I tell Claude about my API patterns
Session 2: Claude has forgotten everything
Session 3: I explain the same patterns again
Session 4: Claude still doesn't remember

3. Guardrails

Without defined constraints, Claude Code may make changes that violate coding standards or architectural decisions.

// Claude might suggest this (wrong for my project)
const userData = await db.query('SELECT * FROM users WHERE id = ' + userId)
// My project requires this (parameterized queries)
const userData = await db.query('SELECT * FROM users WHERE id = $1', [userId])

4. Tool Integration

The base installation doesn’t connect to development tools, documentation sources, or external services.

Without MCP servers, Claude cannot:
- Query my database for context
- Read my API documentation
- Access real-time package information
- Interact with my development tools

Skills: Encoding Project Knowledge

Skills are reusable prompt templates that encode project knowledge and coding patterns. They act as custom instructions that Claude Code applies automatically.

Creating a Skill

I created my first skill in ~/.claude/skills/coding-standards.md:

~/.claude/skills/coding-standards.md
# Coding Standards Skill
## Naming Conventions
- Use camelCase for variables and functions
- Use PascalCase for React components
- Prefix private methods with underscore
## File Structure
- Components in /src/components/{ComponentName}/
- Utilities in /src/utils/
- Types in /src/types/
## API Patterns
- Always use TypeScript
- Validate inputs with Zod schemas
- Return typed ApiResponse<T> wrapper
## Error Handling
- Never catch and swallow errors silently
- Always log errors with context
- Throw user-friendly error messages

How Skills Help

When I ask Claude to implement a feature, it now follows my standards automatically:

// Before skill (generic, doesn't match my project)
function getUser(id) {
return fetch('/api/user/' + id)
}
// After skill (matches my project's patterns)
async function getUser(id: string): Promise<ApiResponse<User>> {
const validated = z.string().uuid().parse(id)
try {
const response = await fetch(`/api/user/${validated}`)
const data = await response.json()
return { success: true, data }
} catch (error) {
console.error('Failed to fetch user:', error)
throw new Error('Unable to retrieve user information')
}
}

Skill Directory Structure

I organized my skills by category:

~/.claude/skills/
├── coding-standards.md # Project conventions
├── api-patterns.md # API development patterns
├── testing-requirements.md # Test standards
├── react-components.md # React component patterns
└── database-patterns.md # Database access patterns

MCP Servers: Real-Time Tool Access

Model Context Protocol (MCP) servers provide Claude Code with access to external tools and data sources. They enable real-time context retrieval and tool execution.

Understanding MCP Architecture

+------------------+ +------------------+ +------------------+
| Claude Code | | MCP Server | | External |
| requests info | --> | (bridge) | --> | Resource |
+------------------+ +------------------+ +------------------+
| | |
v v v
"What methods "Query documentation "findByEmail(),
does UserAPI server for UserAPI" create(), update()"
have?" | |
v v
"Return method [Documentation
signatures" database]

Types of MCP Servers I Use

Documentation servers: Fetch current API docs, framework references

~/.claude/mcp_servers.json
{
"servers": {
"context7": {
"command": "context7-mcp",
"args": ["--docs-dir", "./docs", "--frameworks", "react,typescript"]
}
}
}

Codebase servers: Index and search my project files

~/.claude/mcp_servers.json
{
"servers": {
"codebase": {
"command": "codebase-mcp",
"args": ["--project", "."]
}
}
}

Web fetch servers: Retrieve live web content

~/.claude/mcp_servers.json
{
"servers": {
"fetch": {
"command": "fetch-mcp",
"args": []
}
}
}

Combined MCP Configuration

My complete MCP server setup:

~/.claude/mcp_servers.json
{
"servers": {
"context7": {
"command": "context7-mcp",
"args": ["--docs-dir", "./docs"],
"env": {
"DOCS_PATHS": "./node_modules/*/README.md,./docs"
}
},
"database": {
"command": "postgres-mcp",
"env": {
"DATABASE_URL": "${DATABASE_URL}"
}
},
"git": {
"command": "git-mcp",
"args": ["--repo", "."]
}
}
}

Project Context: The CLAUDE.md File

I created a CLAUDE.md file in my project root to provide project-specific context:

CLAUDE.md (project root)
# Project Context
## Tech Stack
- Frontend: React 18 with TypeScript
- Backend: Node.js with Express
- Database: PostgreSQL with Prisma ORM
- Testing: Jest, React Testing Library, Playwright
## Coding Standards
- Functional components with hooks
- Immutable state updates (use spread operators)
- Zod for runtime validation
- Repository pattern for data access
## Common Patterns
- ApiResponse<T> wrapper for all API responses
- useDebounce hook for input handling
- Error handling with try/catch and meaningful messages
## Key Architecture Decisions
- Use repository pattern for data access
- Never mutate state directly
- All API responses wrapped in ApiResponse<T>
- Rate limiting on all endpoints

Now when I start a session, Claude immediately understands my project.

Guardrails: Preventing Bad Changes

I configured guardrails to prevent undesirable modifications:

~/.claude/settings.json
{
"guardrails": {
"preventFileDeletion": [".env", "*.key", "*.pem", "migrations/*"],
"preventFileModification": ["package-lock.json", "yarn.lock"],
"requireConfirmationFor": [
"git push",
"npm publish",
"database migrations",
"deleting files"
],
"maxChangesPerRequest": 10,
"preventCommands": [
"rm -rf",
"DROP TABLE",
"DROP DATABASE"
]
}
}

How Guardrails Help

When I accidentally ask for something dangerous:

Me: Delete all the old migration files
Claude: I notice this request would delete files matching the pattern
"migrations/*", which is protected by your guardrails. This action
cannot be undone. Would you like me to:
1. List the files that would be deleted (safe)
2. Archive them instead (safer)
3. Proceed with deletion (requires explicit confirmation)

Memory Configuration: Retaining Context

I enabled memory to help Claude retain context across sessions:

~/.claude/settings.json
{
"memory": {
"enabled": true,
"maxEntries": 100,
"includeProjectPatterns": true,
"rememberDecisions": true,
"rememberPreferences": true
}
}

With memory enabled, Claude remembers:

Session 1:
Me: In this project, we use Zod for all validation
Claude: I'll remember to use Zod for validation in this project
Session 2 (new session):
Me: Add validation to the user registration endpoint
Claude: I'll use Zod schemas as per your project's validation pattern...

Complete Configuration Example

Here’s my complete setup that transformed Claude Code from frustrating to powerful:

Project CLAUDE.md:

CLAUDE.md
# Project Context
## Overview
E-commerce platform with React frontend and Node.js backend.
## Tech Stack
- Frontend: React 18, TypeScript, Tailwind CSS
- Backend: Node.js, Express, PostgreSQL
- ORM: Prisma
- Testing: Jest, Playwright
## Architecture
- Repository pattern for data access
- Service layer for business logic
- Controller layer for HTTP handling
## Naming Conventions
- Components: PascalCase (UserCard.tsx)
- Utilities: camelCase (formatDate.ts)
- Types: PascalCase with Type suffix (UserType.ts)
## API Response Format
```typescript
interface ApiResponse<T> {
success: boolean
data?: T
error?: string
meta?: { total: number; page: number; limit: number }
}
**Skills Configuration:**
```markdown title="~/.claude/skills/api-development.md"
# API Development Skill
## Before Creating Endpoints
1. Define Zod schema for input validation
2. Create TypeScript types from schema
3. Implement repository method
4. Add error handling
## Standard Response Format
```typescript
interface ApiResponse<T> {
success: boolean
data?: T
error?: string
meta?: { total: number; page: number; limit: number }
}

Required Checks

  • Input validated with Zod
  • Errors caught and logged
  • Response typed correctly
  • Repository pattern used
**MCP Server Configuration:**
```json title="~/.claude/mcp_servers.json"
{
"servers": {
"context7": {
"command": "context7-server",
"env": {
"DOCS_PATHS": "./node_modules/*/README.md,./docs"
}
},
"database": {
"command": "postgres-mcp",
"env": {
"DATABASE_URL": "${DATABASE_URL}"
}
},
"git": {
"command": "git-mcp",
"args": ["--repo", "."]
}
}
}

Settings Configuration:

~/.claude/settings.json
{
"memory": {
"enabled": true,
"maxEntries": 100,
"includeProjectPatterns": true
},
"guardrails": {
"preventFileDeletion": [".env", "*.key", "migrations/*"],
"requireConfirmationFor": ["git push", "npm publish"],
"maxChangesPerRequest": 10
}
}

Before and After

Here’s how my experience changed:

Before configuration:

Me: Implement a login function
Claude: [Generates generic code that doesn't match my project's patterns,
uses wrong API methods, ignores my error handling conventions]

After configuration:

Me: Implement a login function
Claude: I'll create a login endpoint using your project's patterns:
- Input validation with Zod (as per coding-standards skill)
- Repository pattern for UserAPI (matching CLAUDE.md architecture)
- ApiResponse<T> wrapper (following your API patterns)
- Proper error logging with context
[Generates code that perfectly matches project standards]

Common Mistakes I Made

Mistake 1: Using Claude Code Without Configuration

BAD: Install Claude Code and start coding immediately
GOOD: Set up CLAUDE.md, skills, and MCP servers first

Mistake 2: Copying Skills From Others Without Customization

BAD: Use generic skills from GitHub
GOOD: Create skills that reflect MY project's patterns

Mistake 3: Ignoring MCP Server Setup

BAD: Assume Claude knows about my APIs
GOOD: Configure MCP servers for real-time documentation access

Mistake 4: Setting Guardrails Too Tight

BAD: Require confirmation for every file edit
GOOD: Require confirmation only for dangerous operations (git push, delete)

Mistake 5: Not Updating Configuration

BAD: Set up configuration once and forget it
GOOD: Update skills and CLAUDE.md as project evolves

Configuration Checklist

I now follow this checklist for every new project:

[ ] Create CLAUDE.md with project context
[ ] Set up skills directory with project patterns
[ ] Configure MCP servers for documentation access
[ ] Enable memory for cross-session context
[ ] Set appropriate guardrails
[ ] Test configuration with simple request
[ ] Update configuration as project evolves

Summary

Claude Code’s disappointing default experience isn’t a capability issue; it’s a configuration issue. Codex handles many configuration tasks automatically, while Claude Code expects explicit setup.

The practices that transformed my experience:

  1. Project Context — Create CLAUDE.md with architecture, patterns, and conventions
  2. Skills — Encode project-specific patterns in reusable skill files
  3. MCP Servers — Enable real-time access to documentation and tools
  4. Memory — Retain context across sessions
  5. Guardrails — Prevent dangerous changes and require confirmation for critical operations

The investment in configuration pays dividends across every future coding session. Unlike tools that work well immediately but remain static, Claude Code’s modular approach allows continuous refinement and customization.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments