Building Custom Claude Code Skills: From Concept to Production
A comprehensive guide to developing, testing, and distributing production-ready Claude Code skills that extend AI capabilities for your engineering team.
What Are Skills?
Claude Code skills are packaged capabilities that extend what Claude Code can do. A skill takes a complex, multi-step workflow and encodes it into a reusable command that any team member can invoke.
Consider this scenario: Every time a developer creates a new API endpoint, they need to:
- Generate the route handler with proper error handling
- Create TypeScript types for request/response
- Add input validation
- Write unit tests
- Generate API documentation
- Update the OpenAPI specification
Without a skill, each developer approaches this differently. Some skip steps. Some do them in different orders. Quality varies wildly.
With a skill, they invoke /create-api-endpoint and get consistent, complete outputs every time. The skill encodes your team’s best practices and enforces them automatically.
Skills vs. Prompts vs. Workflows
Understanding the distinction helps you choose the right tool:
Prompts are single instructions. They tell Claude what to do in one interaction.
Review this code for security vulnerabilities.
Templates are structured prompts with placeholders. They standardize how prompts are written.
Review [CODE] for [CATEGORY] issues following [STANDARDS].
Skills are complete workflows that may span multiple interactions, include tool calls, access external systems, and produce complex outputs.
/security-audit
→ Reads codebase structure
→ Identifies security-relevant files
→ Analyzes each for vulnerabilities
→ Cross-references against OWASP guidelines
→ Generates prioritized report with fixes
Workflows orchestrate multiple skills into larger processes.
/release-prep
→ Runs /changelog-generator
→ Runs /security-audit
→ Runs /dependency-check
→ Runs /documentation-update
→ Produces release readiness report
The Skill Development Lifecycle
Building production-ready skills follows a structured process. Skipping steps leads to skills that work in demos but fail in real usage.
Phase 1: Ideation and Validation
Not every workflow should become a skill. Good skill candidates have these characteristics:
High Frequency: Workflows that happen daily or weekly across the team
- API endpoint creation
- Code review assistance
- Documentation generation
- Test scaffolding
High Value: Workflows where consistency matters significantly
- Security audits
- Compliance checks
- Release preparation
- Incident response
High Complexity: Workflows with many steps that are easy to do wrong
- Database migrations
- Dependency upgrades
- Refactoring campaigns
- System integrations
Validation Questions:
- How often does this workflow happen? (Daily = high priority)
- What’s the cost of doing it inconsistently? (Security = high cost)
- How much time does it currently take? (>30 min = good candidate)
- Can it be reasonably automated? (Some tasks need human judgment)
Phase 2: Design and Prototyping
Before writing the skill, design its interface and behavior.
Input Design: What information does the skill need?
skill: create-api-endpoint
inputs:
required:
- name: endpoint_path
type: string
description: "API path (e.g., /users/:id)"
- name: method
type: enum[GET, POST, PUT, DELETE, PATCH]
description: "HTTP method"
optional:
- name: auth_required
type: boolean
default: true
description: "Whether authentication is required"
- name: rate_limit
type: number
default: 100
description: "Requests per minute limit"
Output Design: What does the skill produce?
outputs:
files:
- path: "src/routes/{endpoint}.ts"
type: "route_handler"
- path: "src/types/{endpoint}.ts"
type: "typescript_types"
- path: "tests/{endpoint}.test.ts"
type: "unit_tests"
- path: "docs/api/{endpoint}.md"
type: "documentation"
updates:
- path: "openapi.yaml"
section: "paths"
Error Handling Design: What can go wrong?
error_conditions:
- condition: "endpoint_already_exists"
message: "Endpoint {path} already exists. Use /update-endpoint instead."
recovery: "suggest_alternative"
- condition: "invalid_path_format"
message: "Path must start with / and use :param for parameters"
recovery: "prompt_for_correction"
- condition: "missing_dependencies"
message: "Required package {package} not found in package.json"
recovery: "offer_to_install"
Prototype: Create a minimal version to validate the approach.
# create-api-endpoint (v0.1 prototype)
## Trigger
User invokes: /create-api-endpoint [path] [method]
## Steps
1. Parse inputs and validate format
2. Check if endpoint exists
3. Generate route handler from template
4. Generate types from handler
5. Generate basic test file
6. Output summary
## Notes for v1.0
- Add OpenAPI integration
- Add full validation logic generation
- Add rate limiting setup
Phase 3: Implementation
With the design validated, implement the full skill.
Skill File Structure:
---
name: create-api-endpoint
version: 1.0.0
description: Creates a complete API endpoint with types, tests, and docs
author: platform-team
tags: [api, scaffolding, typescript]
---
# Create API Endpoint
## Trigger Conditions
This skill activates when:
- User invokes `/create-api-endpoint`
- User asks to "create a new API endpoint"
- User asks to "add an endpoint for [resource]"
## Input Handling
### Required Inputs
Request from user if not provided:
- **endpoint_path**: The API path (e.g., `/users/:id`)
- **http_method**: GET, POST, PUT, DELETE, or PATCH
### Optional Inputs
Use defaults if not specified:
- **auth_required**: true
- **rate_limit**: 100 requests/minute
- **response_type**: json
### Validation Rules
- Path must start with `/`
- Path parameters use `:paramName` format
- Method must be valid HTTP method
## Execution Steps
### Step 1: Gather Context
<tool>read_file</tool>
- Read `src/routes/index.ts` to understand routing patterns
- Read `src/middleware/auth.ts` for authentication setup
- Read existing endpoint for pattern matching
### Step 2: Generate Route Handler
Create `src/routes/{resource}.ts`:
```typescript
// Template with team patterns:
// - Error handling with custom ApiError
// - Request validation with zod
// - Response typing
// - Logging integration
Step 3: Generate Types
Create src/types/{resource}.ts:
// Request and response types
// Zod schemas for validation
// Export for use in tests
Step 4: Generate Tests
Create tests/routes/{resource}.test.ts:
// Test structure matching team patterns
// Happy path tests
// Error case tests
// Auth tests if auth_required
Step 5: Update OpenAPI
Modify openapi.yaml:
- Add path definition
- Add request/response schemas
- Add authentication requirements
Step 6: Summary Output
Provide:
- List of created/modified files
- Next steps for the developer
- Any manual steps required
Error Recovery
Endpoint Exists
If endpoint already exists:
- Show existing implementation
- Offer to update instead
- Offer to create with different path
Validation Failed
If inputs don’t validate:
- Explain what’s wrong
- Show correct format
- Ask for corrected input
Missing Dependencies
If required packages missing:
- List missing packages
- Offer to add to package.json
- Provide installation command
Output Format
Success Output
✅ Created API endpoint: POST /users
Files created:
📄 src/routes/users.ts
📄 src/types/users.ts
📄 tests/routes/users.test.ts
Files modified:
📝 openapi.yaml (added path definition)
Next steps:
1. Review generated code
2. Run tests: npm test -- users
3. Start server: npm run dev
Error Output
❌ Could not create endpoint
Issue: Endpoint POST /users already exists
Location: src/routes/users.ts:45
Options:
1. Update existing endpoint: /update-endpoint /users POST
2. Create with different path: /create-api-endpoint /users/new POST
3. Cancel
### Phase 4: Testing
Skills need testing just like code.
**Unit Testing**: Test individual skill components
```typescript
describe('create-api-endpoint skill', () => {
describe('input validation', () => {
it('accepts valid path format', () => {
expect(validatePath('/users/:id')).toBe(true);
});
it('rejects path without leading slash', () => {
expect(validatePath('users/:id')).toBe(false);
});
it('rejects invalid HTTP methods', () => {
expect(validateMethod('INVALID')).toBe(false);
});
});
describe('endpoint generation', () => {
it('generates correct file structure', async () => {
const result = await generateEndpoint({
path: '/users/:id',
method: 'GET'
});
expect(result.files).toContain('src/routes/users.ts');
expect(result.files).toContain('src/types/users.ts');
expect(result.files).toContain('tests/routes/users.test.ts');
});
});
});
Integration Testing: Test the skill in realistic scenarios
describe('create-api-endpoint integration', () => {
beforeEach(async () => {
// Set up test project structure
await createTestProject();
});
it('creates complete endpoint in existing project', async () => {
const result = await invokeSkill('create-api-endpoint', {
path: '/products/:id',
method: 'GET'
});
// Verify files exist
expect(await fileExists('src/routes/products.ts')).toBe(true);
// Verify code compiles
expect(await runTypeCheck()).toPass();
// Verify tests pass
expect(await runTests('products')).toPass();
});
});
Scenario Testing: Test real-world usage patterns
## Test Scenarios
### Scenario 1: New developer creates first endpoint
Setup: Fresh project clone
Action: /create-api-endpoint /health GET --no-auth
Expected: Health check endpoint with no auth requirement
### Scenario 2: Adding endpoint to existing API
Setup: Project with 5 existing endpoints
Action: /create-api-endpoint /orders POST
Expected: Order endpoint following existing patterns
### Scenario 3: Duplicate endpoint handling
Setup: Project with existing /users endpoint
Action: /create-api-endpoint /users GET
Expected: Clear error, offer alternatives
### Scenario 4: Complex nested path
Setup: Standard project
Action: /create-api-endpoint /organizations/:orgId/teams/:teamId/members POST
Expected: Correctly nested handlers and types
Phase 5: Documentation
Skills need documentation for users and maintainers.
User Documentation:
# Create API Endpoint
Creates a complete API endpoint with TypeScript types, tests, and documentation.
## Usage
/create-api-endpoint /path METHOD [options]
## Examples
Basic GET endpoint:
/create-api-endpoint /users GET
POST endpoint without auth:
/create-api-endpoint /public/webhook POST —no-auth
Endpoint with custom rate limit:
/create-api-endpoint /search GET —rate-limit 10
## Options
| Option | Default | Description |
|--------|---------|-------------|
| --no-auth | false | Skip authentication requirement |
| --rate-limit N | 100 | Requests per minute |
| --no-tests | false | Skip test generation |
## Generated Files
- `src/routes/{resource}.ts` - Route handler
- `src/types/{resource}.ts` - TypeScript types
- `tests/routes/{resource}.test.ts` - Unit tests
- Updates `openapi.yaml` with endpoint definition
## Troubleshooting
**"Endpoint already exists"**
Use `/update-endpoint` to modify existing endpoints.
**"Invalid path format"**
Paths must start with `/` and use `:param` for variables.
Maintainer Documentation:
# Create API Endpoint - Maintainer Guide
## Architecture
The skill consists of:
- `skill.md` - Main skill definition
- `templates/` - Code generation templates
- `validators/` - Input validation logic
- `tests/` - Test suite
## Templates
Templates use Handlebars syntax:
- `{{endpoint_name}}` - Resource name from path
- `{{method}}` - HTTP method
- `{{#if auth_required}}` - Conditional blocks
## Modifying Templates
1. Update template in `templates/`
2. Update version (minor for additions, major for breaking changes)
3. Add migration notes if breaking
4. Run full test suite
5. Update documentation
## Common Issues
**Generated code doesn't match new patterns**
Update templates and bump major version.
**Tests fail on certain project structures**
Add project structure detection in Step 1.
Phase 6: Distribution
Getting skills to developers requires a distribution strategy.
Repository Distribution:
skills-library/
├── official/
│ ├── create-api-endpoint/
│ │ ├── skill.md
│ │ ├── README.md
│ │ └── CHANGELOG.md
│ └── ...
├── community/
│ └── ...
└── experimental/
└── ...
Registry Distribution:
{
"name": "create-api-endpoint",
"version": "1.2.0",
"description": "Creates complete API endpoints",
"author": "platform-team",
"repository": "https://github.com/company/skills",
"downloads": {
"skill": "skills/create-api-endpoint/skill.md",
"templates": "skills/create-api-endpoint/templates/"
},
"dependencies": {
"mcp-servers": ["project-context"]
}
}
Automatic Updates:
# .claude/skills.yaml
sources:
- name: company-skills
url: https://skills.internal.company.com
auth: oauth
auto_update: minor # Auto-update minor versions
installed:
- name: create-api-endpoint
version: "^1.2.0"
source: company-skills
Best Practices
Error Handling
Skills must handle errors gracefully. Users should never see cryptic failures.
Principle: Every error should explain what went wrong, why, and how to fix it.
## Error: Cannot read project configuration
What happened:
Could not find package.json in current directory.
Why this matters:
This skill needs to understand your project structure to generate
code that matches your patterns.
How to fix:
1. Make sure you're in the project root directory
2. Run: cd /path/to/your/project
3. Try the command again
Alternative:
Specify the project path: /create-api-endpoint /users GET --project /path/to/project
Versioning
Use semantic versioning for skills:
- Major (2.0.0): Breaking changes to inputs, outputs, or behavior
- Minor (1.1.0): New features, backward compatible
- Patch (1.0.1): Bug fixes, documentation updates
Changelog:
# Changelog
## [1.2.0] - 2025-01-09
### Added
- Support for nested paths (/orgs/:id/teams)
- Rate limiting configuration option
### Changed
- Improved error messages for invalid paths
## [1.1.0] - 2025-01-05
### Added
- OpenAPI automatic updates
## [1.0.0] - 2025-01-01
### Added
- Initial release
Backward Compatibility
When updating skills, maintain backward compatibility:
- Don’t remove inputs - Deprecate them instead
- Don’t change output locations - Add new outputs alongside old
- Don’t change default behavior - Use feature flags for new behavior
- Provide migration paths - Document how to update usage
Performance
Skills should be responsive. Long-running operations need progress indication.
## Progress Indication
For operations taking >5 seconds, show progress:
⏳ Analyzing project structure... (step 1/5)
⏳ Generating route handler... (step 2/5)
⏳ Creating TypeScript types... (step 3/5)
⏳ Writing test scaffolds... (step 4/5)
⏳ Updating OpenAPI spec... (step 5/5)
✅ Complete!
Getting Started
Ready to build your first skill?
- Identify a candidate: Pick a workflow you do frequently that would benefit from standardization
- Design first: Sketch inputs, outputs, and error cases before implementing
- Start simple: Build a minimal version, then iterate
- Test thoroughly: Skills need testing just like production code
- Document well: Users and maintainers need clear documentation
For teams wanting structured guidance on building skill libraries, learn about our consulting services or explore our guide on Claude Code Plugin Architecture.
This article is part of our plugin architecture series. For integration with external systems, see MCP Server Integration Guide. For prompt standardization, read Prompt Templates for Development Teams.