In this Claude Code tutorial you will build a real, shipped CLI tool: a small Python utility that reads a CSV of client invoices, flags overdue ones, and writes a clean summary report. By the end you will have a working binary on your machine, a CLAUDE.md that earns its keep, tests that run, an MCP server wired to live data, and a deploy command. The goal is the workflow, not the artifact. Once you can ship one small tool with Claude Code, you can ship anything. At Formaum, this is the same workflow I use to ship real client builds . Sized down to fit a tutorial.
Most Claude Code tutorials stop at claude --help. This one ships something you can use on Monday.
What you'll build
A CLI called invoice-triage. You run it like this:
invoice-triage ./invoices.csv --days-overdue 14
It reads the CSV, finds every invoice past due by more than 14 days, groups them by client, and writes report.md with totals, the worst offenders, and a follow-up draft for each one. Small, but real. The same skeleton scales to anything: lead triage, log audits, deploy preflight, AI agent harnesses.
You will also wire an MCP server so the same tool can pull live invoices from your accounting platform instead of a CSV. That is the step that turns a script into infrastructure.
Prerequisites
Three things.
1. A Claude subscription (Pro at $20/mo, Max at $100, or Console credits). Free tier won't carry you through a real tutorial session.
2. Python 3.11 or newer. Check with python3 --version.
3. Basic terminal comfort. cd, ls, mkdir. If those are familiar you are fine.
No prior Claude Code experience required.
Step 1: Set up Claude Code
One command installs the CLI. Native install, no Node version mess.
curl -fsSL https://claude.ai/install.sh | bash
Then verify:
claude --version
If you want the full install walkthrough including auth flow and IDE extensions, I wrote that here: How to install Claude Code. For this tutorial, the one-liner above is enough.
Step 2: Create the project skeleton and CLAUDE.md
This is the step every other tutorial skips. The skeleton you start with shapes every prompt that follows.
mkdir invoice-triage && cd invoice-triage
git init
mkdir -p src tests .claude/commands
touch CLAUDE.md README.md src/__init__.py tests/__init__.py
Now write the CLAUDE.md. This file is auto-loaded into context every time Claude Code starts inside this directory. Three sections only. Stack, rules, deploy.
# Project: invoice-triage
## Stack
- Python 3.11+
- click for CLI parsing
- pandas for CSV handling
- pytest for tests
- ruff for lint
## Rules
- All public functions have type hints and a one-line docstring.
- Every CLI flag has a test.
- No network calls in core logic. MCP layer handles live data.
- Tests must pass before commit.
## Deploy
- pipx install . From the project root.
- Binary name: invoice-triage
Keep it short. Do not write a 600-line CLAUDE.md. The rules a human cannot infer from reading the repo are the only rules worth writing down.
Step 3: First prompt and reviewing what Claude does
Start Claude Code in the project directory.
claude
Hit Shift+Tab to cycle into Plan Mode. In Plan Mode, Claude reads but cannot write. This is where you catch bad approaches before they cost you anything.
First prompt:
> Read CLAUDE.md, then propose a plan for the CLI:
- reads a CSV with columns invoice_id, client, amount, due_date, status
- flags any unpaid invoices past --days-overdue
- groups by client, writes report.md with totals and a follow-up draft per client
Plan only. No code yet.
Claude will propose a file layout, the CLI signature, the data model, and a test plan. Read it. Push back on anything that looks off.
> Why pandas instead of csv from the stdlib for a file this small?
> Move the report writer into its own module. I want it testable in isolation.
This is the most important habit in this whole tutorial. Treat Plan Mode as an architecture conversation. The first plan is rarely the best plan. Two rounds of pushback usually gets you to the right one. If you are still arguing after three, the context is poisoned. Hit /clear and reframe the problem.
Step 4: Iterating with the correction-restart loop
Once the plan is right, exit Plan Mode (Shift+Tab again) and let Claude build.
> Implement the plan. Start with src/triage.py (core logic), then
src/report.py (writer), then src/cli.py (click entry point).
One file at a time. Show me each diff before moving on.
Claude will propose a diff. Approve it. Next file. Approve. Next file. Approve. After two or three approvals on the same pattern, you can switch to Accept Edits mode through /permissions so it stops asking. Do not switch on the first diff. Build the trust first.
You will hit a bad output. It happens every session. Here is the rule that saves you hours: if Claude is correcting itself twice on the same task, the context is broken. Stop, /clear, and restart with sharper framing.
Example of what bad correction looks like:
> The follow-up draft is too generic. Make it more specific.
[Claude rewrites it. Still generic.]
> No, I want it to reference the actual invoice IDs.
[Claude rewrites. Now it references IDs but loses the tone.]
Stop here. Do not do a third correction. Run /clear, then come back with:
> Rewrite src/report.py format_followup() so the draft references
every invoice ID by number in the body, keeps a polite tone,
and never exceeds 80 words. Show me the function only.
Specific. Bounded. Includes the constraints up front. That single prompt usually beats five rounds of correction.
Step 5: Adding tests
Tests are how you turn a generated script into a tool you trust. Claude is good at writing them if you tell it what to test.
> Write pytest tests for src/triage.py.
Cases:
- empty CSV returns empty list
- one overdue invoice flagged correctly
- one paid invoice not flagged even if past due
- boundary case: exactly --days-overdue old (not flagged)
Use a tmp_path fixture.
Claude writes the tests. Run them:
pytest -q
If anything fails, paste the failure back to Claude and let it fix. Do not try to be clever here. The failure trace plus "fix this" is usually enough.
Add a hook in .claude/settings.json so tests run before every commit Claude proposes:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash(git commit*)",
"hooks": [
{"type": "command", "command": "pytest -q && ruff check ."}
]
}
]
}
}
Now no commit goes through unless tests pass and the lint is clean. This is the step that promotes Claude Code from a code generator to a code shipper.
Step 6: Wiring an MCP server for live data
The CSV version works. Now make the same tool pull live invoices from a real source. MCP (Model Context Protocol) is how you connect Claude Code to outside systems without writing brittle glue.
For this tutorial, use the Airtable MCP server as the live source. The pattern is identical for QuickBooks, Stripe, Xero, your own Postgres.
Install it:
claude mcp add airtable npx -y @modelcontextprotocol/server-airtable
Set your Airtable token as an env var. Then in Claude Code:
> Add an --source=airtable flag to the CLI.
When set, pull invoices from the Airtable Invoices table
instead of a CSV. Same data shape, same triage logic.
Keep the CSV path as the default.
Claude will read the Airtable schema through the MCP server, map it to the existing data model, and add the flag. The core logic does not change. That is the whole point of the rule in CLAUDE.md about keeping network calls out of core logic.
This is the step that separates a tutorial CLI from real infrastructure. Once your tool can read live data, it stops being a script and starts being part of an operation.
Step 7: Shipping it
Add a pyproject.toml if Claude has not already.
> Write pyproject.toml so pipx install . Makes invoice-triage
available globally. Entry point: src.cli:main.
Install it:
pipx install .
Now it runs from anywhere on your machine:
invoice-triage ./invoices.csv --days-overdue 14
invoice-triage --source=airtable --days-overdue 30
Commit and push.
> Commit everything with a clear message and push to main.
You shipped it. That is the workflow. Same pattern for every tool from here on.
What's next
Three follow-ups depending on where you go next.
If you want the deeper daily workflow, read How to use Claude Code: the practitioner's guide. It covers the four-phase loop, project skeletons, and the production discipline this tutorial only touched.
If you want to compare Claude Code to other tools before going further, Claude Code vs Cursor and Codex vs Claude Code cover the tradeoffs.
If you want to wire more MCP servers, Best MCP servers covers the ones I actually use in client work.
Common tutorial mistakes
I read the top-ranked Claude Code tutorials before writing this. Same mistakes in all of them.
Stopping at install. Most tutorials show you the install command and call it a day. Installing the tool is not learning the tool. You learn it by shipping something with it.
Building toy projects with no tests. A temperature converter with no test file is not a tutorial, it is a syntax demo. The test step is the step that builds the habit. Skip it and you stay a hobbyist.
Skipping CLAUDE.md. Without it, every session starts cold and Claude wastes tokens re-discovering your project. The five minutes you spend writing CLAUDE.md saves you hours over the life of the project.
Ignoring Plan Mode. Tutorials mention it as a feature, then never use it. Plan Mode is the single most valuable workflow surface in Claude Code. The first plan is rarely the best one. Pushing back on the plan is cheaper than rebuilding the code.
No MCP step. A Claude Code tutorial without MCP is a tutorial about a chat box. MCP is how you turn it into infrastructure. Wire one server in your first tutorial. Even a small one. It changes how you think about every tool you build after.
Treating correction loops as progress. If Claude is correcting itself twice on the same problem, you are not getting closer. You are getting further. /clear and restart with sharper framing. Cheaper than the third correction.
Run on a stack that's holding you back?
Book a 45-minute discovery call. I'll map what moves, what stays, and what makes sense for your operation.
Book a call