Skip to content

Building on Claude

Claude Code is extensible. You can build custom tools, integrate with your systems, and create workflows that go beyond the built-in capabilities.

MethodUse CaseComplexity
Custom slash commandsTeam workflows, shortcutsLow
MCP serversConnect to external tools/dataMedium
HooksEnforce policies, add automationMedium
SDKProgrammatic controlHigh

The simplest extension—markdown files that become commands.

  • Directory.claude/
    • Directorycommands/
      • deploy.md
      • review.md
      • ticket.md
.claude/commands/deploy.md
Deploy the current branch to staging:
1. Run tests first: `npm test`
2. Build: `npm run build`
3. Deploy: `./scripts/deploy-staging.sh`
4. Verify the deployment at https://staging.example.com
5. Report any errors
Ask for confirmation before deploying.

Usage:

> /deploy
.claude/commands/ticket.md
I'm working on ticket: $ARGUMENTS
1. Read the ticket from our issue tracker (if accessible)
2. Understand the requirements
3. Identify which files need changes
4. Propose an implementation plan
Wait for approval before implementing.

Usage:

> /ticket AUTH-123

MCP servers connect Claude to external tools and data sources.

  • Access internal APIs
  • Query proprietary databases
  • Integrate with internal tools
  • Provide domain-specific context
my_mcp_server.py
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("my-company-tools")
@mcp.tool()
def get_ticket(ticket_id: str) -> str:
"""Fetch a Jira ticket by ID.
Args:
ticket_id: Ticket ID like PROJ-123
"""
ticket = fetch_from_jira(ticket_id)
return json.dumps(ticket)
if __name__ == "__main__":
mcp.run()
Terminal window
claude mcp add my-tools -- python /path/to/my_mcp_server.py
ServerPurpose
Jira/Linear clientFetch ticket context
Internal docs searchQuery your wiki
Database explorerSafe read-only queries
Deployment statusCheck environments
Feature flagsCurrent flag states

Hooks run scripts at key points. See Hooks for full details.

Auto-format on save:

{
"hooks": {
"PostToolUse": [{
"matcher": { "tool": "Write" },
"hooks": [{
"type": "command",
"command": "prettier --write $FILE_PATH"
}]
}]
}
}

Notify on completion:

{
"hooks": {
"Stop": [{
"hooks": [{
"type": "command",
"command": "notify-send 'Claude finished'"
}]
}]
}
}

Log all commands:

{
"hooks": {
"PostToolUse": [{
"matcher": { "tool": "Bash" },
"hooks": [{
"type": "command",
"command": "echo \"$(date): $COMMAND\" >> ~/.claude/command-log.txt"
}]
}]
}
}

The Claude Code SDK enables building tools on top of Claude.

Terminal window
uv add claude-code-sdk
import asyncio
from claude_code_sdk import ClaudeCode
async def main():
claude = ClaudeCode()
# Run a task
result = await claude.run(
prompt="Add input validation to src/forms/user.py",
working_directory="/path/to/project"
)
print(result.output)
asyncio.run(main())
async def stream_example():
claude = ClaudeCode()
async for chunk in claude.stream(prompt="Explain this codebase"):
print(chunk.text, end="")
asyncio.run(stream_example())
async def with_approval():
claude = ClaudeCode()
def on_tool_use(tool):
# Custom approval logic
if tool.name == "Bash" and "rm" in tool.command:
return {"approved": False, "reason": "Destructive commands blocked"}
return {"approved": True}
result = await claude.run(
prompt="Run the test suite",
on_tool_use=on_tool_use
)
asyncio.run(with_approval())

Create specialized tools for your team:

scripts/review_pr.py
#!/usr/bin/env python3
import subprocess
import sys
import asyncio
from claude_code_sdk import ClaudeCode
async def review_pr(pr_number: str):
diff = subprocess.check_output(["gh", "pr", "diff", pr_number], text=True)
claude = ClaudeCode()
result = await claude.run(
prompt=f"""
Review this PR diff for:
- Security issues
- Performance problems
- Code quality
{diff}
""",
model="claude-sonnet-4-5"
)
# Post as PR comment
subprocess.run(["gh", "pr", "comment", pr_number, "--body", result.output])
if __name__ == "__main__":
asyncio.run(review_pr(sys.argv[1]))

Usage:

Terminal window
python scripts/review_pr.py 123
# Process multiple tasks in sequence
import asyncio
from claude_code_sdk import ClaudeCode
async def batch_process(tasks: list[str]):
claude = ClaudeCode()
for task in tasks:
print(f"Processing: {task}")
await claude.run(prompt=task)
asyncio.run(batch_process([
"Add docstrings to src/services/",
"Add type hints to src/models/",
"Update README with new API endpoints"
]))
  • Same workflow repeated daily
  • Team needs standardization
  • Integration with internal systems required
  • Automation in CI/CD needed
  • One-off tasks
  • Exploratory work
  • Tasks requiring human judgment
  • Prototyping/experimentation
Your Script
├── Pre-processing (gather context)
├── Claude Code SDK call
└── Post-processing (parse results, take action)
Task Definition
├── Claude: Planning
├── Claude: Implementation
├── Automated: Testing
└── Claude: Review & Fix
Git Hook / CI Event
└── Trigger Claude workflow
└── Report results back to system