Automated client reporting
Every Monday, a finished report deck lands in an inbox without anyone touching a keyboard. The pipeline pulls the week's metrics from a BI tool, runs three parallel Claude conversations to draft the commentary, validates every number before it is allowed near a slide, and sends the deck itself.

At a glance
- Role
- I designed the pipeline architecture, decomposed the metric set into parallel task groups, wrote the task prompts, and defined every failure boundary.
- Cadence
- Every Monday at 08:00 UTC. No manual trigger, and no human step in the happy path.
- Recipients
- Account leadership and the client, by email, on a fixed distribution list.
- Built on
- The Anthropic API called directly.
- Sources
- A BI tool's saved queries and one ad-hoc SQL query, reached through a governed MCP proxy.
- Writes to
- A copied Google Slides deck, filled by API and emailed out. No document is touched by hand.
The problem
A weekly executive deck every Monday morning.
Nine numbers, every week, by hand
Pulling main performance metrics out of Redash, writing commentary around them, and assembling a deck is the same sequence of steps every Monday. All of it takes a person's morning.
Architecture
Scroll to see the full diagram
Design decisions
Most of the engineering here is in what the system refuses to do.
Split the metric graph, not the code
The nine metrics decompose into three independent groups: eight metrics sharing one saved query on a one-week window, one metric on the same query with a two-week window for week-over-week, and one metric from ad-hoc SQL. None depend on each other, so they run as three concurrent Claude conversations instead of one long one.
No silent fallbacks, anywhere
Every boundary in the pipeline throws with the specific failure identified, and the cron returns 500 rather than sending something incomplete.
A proxy that never holds a credential
The MCP server that executes queries carries no stored credentials. The caller passes them per request as headers, and the pipeline forwards only the two tools it is allowed to use — everything else the proxy offers is filtered out before Claude ever sees the tool list.
Zero-touch auth
Google access uses a refresh-token flow: the access token is exchanged automatically, cached for about an hour, and silently renewed. There is no interactive login anywhere in the runtime path — the only human dependency is granting the OAuth scopes once, up front.
Where it stands
What I did
I designed and built this end to end.
Decomposed the metric set into a dependency graph
Mapping which metrics share a query, a window, or a source is what made the parallel split possible.
Wrote the merge contract
Defined exactly what a valid combined result looks like — no missing metric, no duplicate, no metric claimed by the wrong task — and made that check deterministic code rather than another model call.
Specified every failure boundary before the happy path
Each row in “Where it can fail” was written before the corresponding feature, not discovered after an incident. The rule throughout: fail closed, name the boundary, never guess.
Covered it with tests, not just a live run
The test suite covers week-range math, the prompt builders, the merge contract, MCP client behaviour, environment-variable handling, cron auth, and the email builder — so the pipeline's failure modes are verifiable without waiting for next Monday.