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 codebase2. 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 patternsSession 2: Claude has forgotten everythingSession 3: I explain the same patterns againSession 4: Claude still doesn't remember3. 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 toolsSkills: 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:
# 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 messagesHow 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 patternsMCP 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
{ "servers": { "context7": { "command": "context7-mcp", "args": ["--docs-dir", "./docs", "--frameworks", "react,typescript"] } }}Codebase servers: Index and search my project files
{ "servers": { "codebase": { "command": "codebase-mcp", "args": ["--project", "."] } }}Web fetch servers: Retrieve live web content
{ "servers": { "fetch": { "command": "fetch-mcp", "args": [] } }}Combined MCP Configuration
My complete MCP server setup:
{ "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:
# 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 endpointsNow when I start a session, Claude immediately understands my project.
Guardrails: Preventing Bad Changes
I configured guardrails to prevent undesirable modifications:
{ "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 actioncannot 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:
{ "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 validationClaude: I'll remember to use Zod for validation in this project
Session 2 (new session):Me: Add validation to the user registration endpointClaude: 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:
# Project Context
## OverviewE-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```typescriptinterface 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 Endpoints1. Define Zod schema for input validation2. Create TypeScript types from schema3. Implement repository method4. Add error handling
## Standard Response Format```typescriptinterface 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:
{ "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 immediatelyGOOD: Set up CLAUDE.md, skills, and MCP servers firstMistake 2: Copying Skills From Others Without Customization
BAD: Use generic skills from GitHubGOOD: Create skills that reflect MY project's patternsMistake 3: Ignoring MCP Server Setup
BAD: Assume Claude knows about my APIsGOOD: Configure MCP servers for real-time documentation accessMistake 4: Setting Guardrails Too Tight
BAD: Require confirmation for every file editGOOD: Require confirmation only for dangerous operations (git push, delete)Mistake 5: Not Updating Configuration
BAD: Set up configuration once and forget itGOOD: Update skills and CLAUDE.md as project evolvesConfiguration 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 evolvesSummary
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:
- Project Context — Create CLAUDE.md with architecture, patterns, and conventions
- Skills — Encode project-specific patterns in reusable skill files
- MCP Servers — Enable real-time access to documentation and tools
- Memory — Retain context across sessions
- 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:
- 👨💻 Anthropic Documentation: Claude Code Best Practices
- 👨💻 Model Context Protocol (MCP) Specification
- 👨💻 Claude Skills Documentation
- 👨💻 Reddit Discussion: Claude Code vs Codex Comparison
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments