A solo weekend build on two open tax standards — OTD and OpenTax — where every number on the return keeps a visible line back to the PDF box it came from. Built to learn the open code, not to sell tax software.
Synthetic data only. Every client, K-1 and figure in the demo is made up — it isn't tax advice and nothing is ever sent to the IRS. Independent project, not affiliated with or endorsed by Filed or Crimson Tree Software.
I wanted hands-on time with the Open Tax Technology Alliance's open code — the OTD data standard for K-1s and the OpenTax 1040 engine — and a reason to use the Model Context Protocol for something with real domain complexity. This isn't a Drivkraft LLC product and I have no plan to enter the tax software market with it. It's a fully separate codebase from my actual consulting work, sharing nothing but a coat of paint. OTD and OpenTax are only usable because their authors made them open — putting this build out publicly under MIT, build log included, is my way of contributing back to that same spirit.
The interesting problem wasn't the UI. It was the seam between two standards that were never designed to meet: a document format for describing what's on a K-1, and a calculation engine that decides what to do with it. Building the bridge that lets those two open standards actually talk to each other — with a disposition ledger tracking every value end to end — became the actual point of the build.
Four screens from the live demo, in the order a return actually moves through them.
Every visitor gets their own cookie-scoped set of three cases plus two shared read-only reference cases, seeded ahead of time so the first load is instant. A guided tour walks new visitors through the review screen, the return and the chat panel.
DRIVKRAFT_DEMO=1nightly resetno signup
The review screen puts the source PDF beside the extracted value. Each item carries a disposition — mapped, derived, informational or unverified — and an edit needs a reason and keeps the original. Approval is blocked until every flag is acknowledged; nothing reaches the calculation silently.
keyboard-firstj / k / e / adisposition ledger
OpenTax calculates the 1040. Clicking any line opens a leave-one-out waterfall back through the K-1 boxes and inputs that feed it, and a banner names exactly which K-1 amounts couldn't reach the calculation at all — instead of quietly leaving them out.
line attributionwhat-if scenarios
Tool latency (p50/p95), estimated AI spend against a monthly cap, and every MCP/HTTP call with its error count, pulled straight from the events log. This page is also how I caught a live bug on launch day — see the build log.
p50 / p95spend caperror tracking
Every capability is a plain Python function registered once. The server exposes each one twice — as an MCP tool and as a FastAPI route — so the web app never holds logic that Claude can't also reach.
otd-spec and OpenTax run unmodified, as a subprocess — a data standard and a calculation engine, neither with a UI or any of the layers below. Everything from here down is this build.
The whole visual system — type (Fraunces/IBM Plex), layout, and the case list, K-1 review, return and operator screens — designed and built from scratch. OTD is JSON; OpenTax is a calc binary. Neither renders a pixel.
The translation layer between the two standards, with a per-field audit trail — mapped, derived, informational or unverified — so nothing reaches the calculation silently.
A web chat panel running the same MCP tools the desktop client uses, with anything it proposes landing in an Inbox for review rather than applying itself.
A pluggable slot on each case for a research tool to attach to — built as an extension point, not wired to a live API in this build.
Transcripts turn into proposals plus a per-case checklist, so a meeting's follow-ups have somewhere to live instead of getting lost.
An Excel workpaper round-trip with a cell-level diff, a PDF review packet, a MeF export against a fake IRS transmitter, a batch e-file queue across cases with per-case readiness badges, and a live dashboard watching tool latency, AI spend and errors.
Three sessions, paired end to end with Claude Code, listed here in phase order rather than the order they actually shipped in — see the note below. The clock is a real accounting, not a guess.
Not a round-number guess — summed from git history. 32 commits across three sessions (Sep 25–28, 2026); the gap between each consecutive pair counts as build time, capped at 30 minutes so overnight and weekend breaks between sessions don't inflate it. What's left is 9h 02m 09s of real, accounted work.
Every phase kept its own tests and a planning-doc update; the operator page's live error count above is the demo genuinely catching its own bug post-launch, not a staged example. This list runs in phase order, not build order: Phases 13 and 14 ran as separate, unsynchronized sessions on the same repo, and Phase 13's work actually finished after Phase 14's — it's placed here by its number, not its timestamp.
Your sandbox is private to your browser and resets nightly at 08:00 UTC.
Local (stdio), in claude_desktop_config.json:
{ "mcpServers": { "drivkraft-tax": {
"command": "/path/to/drivkraft-tax/.venv/bin/drivkraft-tax-mcp" } } }Or run it yourself:
git clone https://github.com/michaelrenwick-a11y/Drivkraft-tax scripts/bootstrap.sh uv run drivkraft-tax-server cd web && npm install && npm run dev
Hosted MCP needs an invite code (rate-limited) — ask if you want one.
Drivkraft is Danish for the driving force that powers a machine or carries a business or a person forward — and it's your everyday advisor. It keeps the business owner and their professional advisors aligned and informed, so nothing gets lost between the books, the return and the conversation about what comes next.
Like what you saw here? See what else I've built at drivkraft.io ↗