Claude Code Extensions
Add custom tools to Claude Code with simple YAML definitions
Quick Start • How It Works • Create Extensions • Built-in Tools • CLI Reference
✨ Demo
$ claude "use cowsay to say 'Hello from custom extensions!'"
__________________________________
< Hello from custom extensions! >
----------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
$ claude "roll 4d6 for my D&D character"
Rolled 4d6: [6, 4, 5, 2] = 17
$ claude "what time is it right now?"
It's Monday, January 27, 2025 at 10:42:53 AM.
These tools don't exist in Claude Code. They were added in 30 seconds.
🚀 Quick Start
1. Install
git clone https://github.com/hexcreator/claude-code-extensions.git cd claude-code-extensions npm install npm link # Makes 'claude-ext' available globally
2. Initialize & Start
claude-ext init # Creates ~/.claude-extensions/ claude-ext start # Starts the extension proxy
3. Use with Claude Code
ANTHROPIC_BASE_URL=http://localhost:8892 claude "roll a d20"That's it! Claude Code now has access to custom tools.
🎯 Why This Exists
Claude Code ships with ~30 built-in tools, but what if you need:
| Need | Solution |
|---|---|
| 📢 Slack notifications | claude-ext create SlackNotify |
| 🎲 Dice for D&D | Built-in Dice tool |
| ⏰ Actual current time | Built-in Timestamp tool |
| 🔌 Custom API calls | Built-in HTTPRequest tool |
| 🐄 ASCII art cows | Built-in Cowsay tool |
No MCP servers. No complex protocols. Just YAML + a handler.
⚙️ How It Works
┌─────────────────┐ ┌───────────────────────────────────────┐ ┌─────────────────┐
│ │ │ Extension Proxy │ │ │
│ Claude Code │────▶│ ┌─────────────────────────────────┐ │────▶│ Anthropic API │
│ │ │ │ 1. Inject custom tool schemas │ │ │ │
│ "roll a d20" │ │ │ 2. Forward to Anthropic │ │ │ claude-sonnet │
│ │◀────│ │ 3. Intercept tool calls │ │◀────│ │
│ "You rolled │ │ │ 4. Execute local handlers │ │ │ tool_use:Dice │
│ 17!" │ │ │ 5. Inject results │ │ │ │
│ │ │ └─────────────────────────────────┘ │ │ │
└─────────────────┘ └───────────────────────────────────────┘ └─────────────────┘
│
▼
┌───────────────────────────────┐
│ ~/.claude-extensions/ │
│ ├── tools/ │
│ │ ├── dice.yaml │
│ │ └── cowsay.yaml │
│ └── handlers/ │
│ ├── dice.js │
│ └── cowsay.js │
└───────────────────────────────┘
The proxy intercepts Claude Code's API requests and:
- Adds your tools to the request's tool list
- Watches responses for tool calls
- Executes your handlers locally
- Injects results back into the conversation
Claude thinks the tools are built-in. They're not—they're running on your machine.
📝 Creating Extensions
Method 1: CLI (Recommended)
This scaffolds:
~/.claude-extensions/tools/mytool.yaml- Tool definition~/.claude-extensions/handlers/mytool.js- Handler implementation
Method 2: Manual
1. Create the definition:
# ~/.claude-extensions/tools/slack.yaml name: SlackPost description: Post a message to a Slack channel handler: slack.js schema: properties: channel: type: string description: Channel name (e.g., #general) message: type: string description: Message to post required: - channel - message
2. Create the handler:
// ~/.claude-extensions/handlers/slack.js module.exports = async function(input, context) { const { channel, message } = input; const { env, fetch } = context; await fetch('https://slack.com/api/chat.postMessage', { method: 'POST', headers: { 'Authorization': `Bearer ${env.SLACK_TOKEN}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ channel, text: message }) }); return `Message posted to ${channel}`; };
3. Use it:
claude "post 'Build complete!' to #dev-notifications"🧰 Built-in Tools
🐄 Cowsay
ASCII art cow messages.
schema: message: string # What the cow says style: enum # default, think, yell, dead, etc.
🎲 Dice
Random number generation.
schema: sides: number # Die sides (default: 6) count: number # Number of dice (default: 1) # OR min: number # Range minimum max: number # Range maximum # OR pick: array # Items to randomly select from
⏰ Timestamp
Current date and time.
schema: format: enum # iso, unix, human, date, time timezone: string # e.g., "America/New_York"
🌐 HTTPRequest
Make HTTP requests to any URL.
schema: url: string # Full URL (required) method: enum # GET, POST, PUT, DELETE headers: object # Key-value headers body: string # Request body
💻 CLI Reference
claude-ext <command> [options]
| Command | Description |
|---|---|
init |
Initialize ~/.claude-extensions/ directory |
start |
Start the extension proxy |
start --watch |
Start with hot reload |
list |
List installed extensions |
create <name> |
Create a new extension |
test <name> [json] |
Test a handler directly |
help |
Show help |
Examples
# Create and test a new tool claude-ext create WeatherAPI claude-ext test weatherapi '{"city": "San Francisco"}' # Start proxy with auto-reload claude-ext start --watch # Use with Claude Code ANTHROPIC_BASE_URL=http://localhost:8892 claude "your prompt"
🔧 Handler Interface
JavaScript
module.exports = async function(input, context) { // input: Parameters from tool call // context: { env, log, fetch } const { myParam } = input; const { env, log, fetch } = context; log('Processing:', myParam); return 'Result string'; };
Python
import os, json input_data = json.loads(os.environ['EXTENSION_INPUT']) print(f"Result: {input_data['myParam']}")
Shell
#!/bin/bash # Input params are UPPERCASE env vars echo "Received: $MYPARAM"
🔒 Security Notes
- Handlers run with full system access
- Environment variables can contain secrets
- No sandboxing by default
- Only install extensions you trust
🆚 Comparison with MCP
| MCP | This Framework | |
|---|---|---|
| Setup | JSON-RPC server | YAML + handler |
| Protocol | Must implement | None |
| Learning curve | Steep | Minimal |
| Time to first tool | Hours | Minutes |
| Best for | Production | Quick tools |
Use MCP for robust, production-grade integrations. Use this when you just want a tool and want to move on.
🤝 Contributing
Contributions welcome! Feel free to:
- Add new built-in tools
- Improve the proxy
- Fix bugs
- Enhance documentation
📄 License
MIT © hexcreator
Built by reverse-engineering Claude Code's architecture
See the full research →