Putting It Together: CLI + MCP + Skills
Build a real builder automation by chaining Claude Code's CLI, a custom MCP server, and a skill - the combination that turns repetitive hours into a single command.
TL;DR: Claude Code has three layers of extensibility - the CLI for raw automation, MCP servers for live tool connections, and skills for packaged workflows. Each one is useful alone. Chain them together and you get something that genuinely saves hours every week, not just minutes.
Why three layers?
Most builders start with the Claude Code CLI, ask it to do something, and call it done. That works. But the real leverage comes when you treat the three extensibility layers as composable primitives rather than separate features.
- The CLI is the agent itself - it reads files, runs shell commands, calls tools, and takes instructions as text. You can pipe into it, script it, and run it headlessly in CI.
- MCP servers are live connections to external systems. Instead of copying data into chat from your issue tracker or monitoring dashboard, Claude reads and acts on those systems directly.
- Skills are reusable, self-describing workflows. Write the procedure once as a
SKILL.mdfile, and Claude loads it on demand - either when you invoke it with/skill-name, or automatically when your request matches its description.
The design is deliberate: skills cost almost nothing until used (the description loads into context, the body stays on disk), MCP servers handle the live data that changes constantly, and the CLI glues it all together into something you can schedule, pipe, and automate.
The scenario: a weekly release-readiness check
Here is a concrete example. You ship software. Every week, before a release, you do the same five things manually:
- Check open GitHub issues tagged
release-blocker - Pull the latest Sentry error count for your main service
- Read the diff since last release and write a human-readable summary
- Post a Slack message with the summary and a green/red status
- Open a GitHub PR for the release branch
That is roughly 45 minutes of context-switching each week. With a CLI invocation, a GitHub + Sentry MCP server, and a skill, it becomes one command.
Step 1 - connect the tools with MCP
MCP (Model Context Protocol) is an open standard that defines how AI agents connect to external tools and data. Claude Code supports four transport types: HTTP (recommended for cloud services), SSE (deprecated, avoid for new setups), stdio (local processes), and WebSocket (for persistent bidirectional connections). For cloud services, HTTP is the right choice.
Connect GitHub and Sentry with two CLI commands:
# Add GitHub MCP server (official, hosted by GitHub)
# Note: GitHub also supports OAuth - omit the header to authenticate interactively via /mcp
claude mcp add --transport http github https://api.githubcopilot.com/mcp/ \
--header "Authorization: Bearer YOUR_GITHUB_PAT"
# Add Sentry MCP server (uses OAuth - authenticate via /mcp after adding)
claude mcp add --transport http sentry https://mcp.sentry.dev/mcp
After adding the Sentry server, authenticate by running /mcp inside a Claude Code session and completing the OAuth flow. Verify both servers are live:
claude mcp list
From this point, Claude can call GitHub API endpoints and read Sentry data without you pasting anything into chat. It queries those systems directly as part of its reasoning.
If you need a custom MCP server - for an internal system, a private database, or a bespoke API - you can write one with the official Python or TypeScript SDK. The Python version uses FastMCP, which reads your function signatures and docstrings to generate tool definitions automatically. A minimal tool looks like this:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("my-internal-api")
@mcp.tool()
async def get_deploy_status(service: str) -> str:
"""Get the current deploy status for a service.
Args:
service: Service name (e.g. api, web, worker)
"""
# Call your internal deploy API here
return fetch_deploy_status(service)
if __name__ == "__main__":
mcp.run(transport="stdio")
Register it as a local stdio server:
claude mcp add --transport stdio deploy-status -- python deploy_status.py
The --scope project flag saves any server into .mcp.json at your repo root so the whole team shares the same connections - commit that file to version control and every teammate picks up the server on their next pull.
Step 2 - write the skill
A skill is a directory under ~/.claude/skills/ (personal, available everywhere) or .claude/skills/ (project-scoped). Each skill needs one file: SKILL.md. The directory name becomes the slash command.
Create the release-readiness skill:
mkdir -p ~/.claude/skills/release-check
Then write ~/.claude/skills/release-check/SKILL.md:
---
name: release-check
description: Run a pre-release readiness check. Checks open blockers, error rates, and drafts a Slack summary. Use when preparing a release or asked to check release readiness.
disable-model-invocation: false
allowed-tools: Bash(gh *) mcp__github__* mcp__sentry__*
---
## Current environment
- Branch: !`git branch --show-current`
- Last release tag: !`git describe --tags --abbrev=0`
- Diff since last release: !`git log $(git describe --tags --abbrev=0)..HEAD --oneline`
## Instructions
1. Use the GitHub MCP server to list open issues labeled `release-blocker` in this repo.
2. Use the Sentry MCP server to get the error count for the `production` environment over the last 7 days. Compare it to the prior 7 days.
3. Read the diff summary above and write a 3-5 bullet plain-English summary of what changed.
4. Based on open blockers and error trend, assign a status: GREEN (ship it), YELLOW (ship with caution), or RED (do not ship).
5. Format the output as a Slack message using the template in [template.md](template.md).
6. Print the final Slack message. Ask before posting it.
Add ~/.claude/skills/release-check/template.md:
:rocket: *Release Readiness Check - {DATE}*
*Status:* {STATUS_EMOJI} {STATUS}
*What changed:*
{BULLET_SUMMARY}
*Blockers:* {BLOCKER_COUNT} open
*Error trend:* {ERROR_DELTA}
_Generated by /release-check_
Two things worth noting here. First, the !`git ...` lines use dynamic context injection - Claude Code runs those shell commands before the skill content reaches Claude, so the actual branch name and diff output arrive inline. Claude sees real data, not a placeholder. Second, the allowed-tools field pre-approves the MCP tools and the gh CLI, so Claude does not pause to ask permission mid-run.
Step 3 - add a hook to wire it all together
Hooks run shell commands at specific points in Claude Code's lifecycle. They are deterministic - they always fire, regardless of what Claude decides to do. That is exactly what you want for enforcement and automation.
For the release workflow, a useful hook auto-formats any file Claude edits, so code quality stays consistent even inside a long agent run. Add this to .claude/settings.json in your project:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write 2>/dev/null || true"
}
]
}
],
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code needs input\" with title \"Release Check\"'"
}
]
}
]
}
}
The PostToolUse hook fires after every file edit and runs Prettier. The Notification hook fires when Claude is waiting for your approval - in this case, before it posts to Slack - so you get a desktop alert instead of staring at the terminal.
Step 4 - run the whole chain
With MCP servers connected, the skill written, and hooks configured, the release check runs like this:
# Interactive
claude
/release-check
# Or headless, piped from CI or a cron job
claude -p "/release-check" --output-format text
What happens under the hood:
- Claude Code reads the skill description and loads
SKILL.md - The
!`git ...`injections run immediately, inserting real branch and diff data - Claude calls the GitHub MCP server to fetch blocker issues
- Claude calls the Sentry MCP server to read error metrics
- Claude synthesizes everything into the Slack template and prints it
- The
Notificationhook fires: you get a desktop alert - You approve or edit the message, Claude posts it
The first time you run it, expect to spend 10-15 minutes tuning the skill instructions and the template. After that, the whole thing takes about 90 seconds and one keystroke.
How to think about the three layers
The mental model that makes this composable:
- MCP = what Claude can see and touch. External systems, live data, APIs that change. If you would normally copy-paste from a dashboard, an MCP server eliminates that step.
- Skills = what Claude knows how to do. Multi-step procedures, project conventions, output formats. If you keep re-explaining a process, it belongs in a skill.
- Hooks = what always happens, no matter what. Formatting, notifications, blocking dangerous commands, enforcing rules. Hooks are deterministic where Claude is probabilistic.
- The CLI = the execution surface. The thing that runs everything and can be scripted, scheduled, piped, and automated.
You do not need all four every time. A simple project might only use a skill and the CLI. A complex team workflow might use MCP + skills + hooks + a cron-scheduled routine running on Anthropic's cloud infrastructure so it keeps running while your machine is off. Start with the minimum that solves the problem and add layers when you actually hit the limit.
Key takeaways
- MCP servers connect Claude to live systems. Use
claude mcp add --transport httpfor cloud services, stdio for local scripts. The Anthropic Directory atclaude.ai/directorylists reviewed connectors you can add with a single command. Remote cloud servers typically use OAuth - run/mcpinside a session to authenticate after adding the server. - Skills load on demand - only the description costs tokens until you invoke them. Use frontmatter to control who triggers them (
disable-model-invocation: truekeeps skills user-only and prevents automatic subagent preloading), pre-approve tools withallowed-tools, and use!`command`to inject live shell output before Claude reads the skill. - Hooks are the deterministic layer. Configure
PostToolUsefor auto-formatting,Notificationfor alerts, andPreToolUsefor blocking operations you never want Claude to run unsupervised. - The real payoff is composition. One CLI command that chains MCP data, skill instructions, and hook automation turns a 45-minute weekly task into 90 seconds.
- Scope your config intentionally. Skills in
~/.claude/skills/are personal and always available. MCP servers added with--scope projectwrite to.mcp.jsonin the repo root and are shared with your team via version control.
Try this next
Once your first chained workflow is running, the natural next step is writing custom MCP servers for your own internal APIs. See Build a Custom MCP Server for a step-by-step guide to scaffolding, testing, and registering a server Claude Code can call like any other tool.