All work

Case study · 02 / 04

BambooHR MCP Server

A custom Model Context Protocol server exposing BambooHR HR systems to Claude Desktop.

Role
Sole engineer
Timeline
2025
Status
Shipped

The problem

HR data lives in BambooHR — but the questions people actually ask (“who joined this quarter”, “whose probation review is due”, “draft the offboarding email for X”) never live there. Pulling answers means switching tabs, copying values, and rephrasing them by hand.

Claude Desktop can answer those questions if it can read BambooHR — but only through a Model Context Protocol server that speaks both sides correctly: BambooHR’s API on one end, MCP’s tool/resource contract on the other.

My approach

A small, strictly-typed Node.js MCP server. Each BambooHR endpoint becomes a typed MCP tool with a Zod-validated input schema, a narrow output shape, and a usage description Claude can read at runtime. Sensitive operations are read-only by default; mutating tools are opt-in via env flag.

Authentication is handled out-of-band via BambooHR’s API key model so the server never sees user credentials. Rate limiting and retry-with-backoff are baked into the HTTP layer. Errors are translated into MCP error shapes with actionable messages so Claude can recover or ask the user for clarification rather than silently fail.

Delivered

What I shipped.

  1. 01

    Strictly-typed MCP tool catalog

    Each BambooHR endpoint exposed as a discoverable MCP tool with Zod input schemas and short, copy-edited descriptions Claude reads at session start.

  2. 02

    Read-only by default

    Mutating endpoints are gated behind an explicit flag — safe to install in personal Claude Desktop without risking accidental writes.

  3. 03

    Resilient HTTP layer

    Retry-with-backoff, request rate limiting, structured error translation into MCP error shapes.

  4. 04

    Local-first install

    npx-runnable so a non-engineer can drop one line into their Claude Desktop config and be productive in minutes.

Decisions

Worth calling out.

  1. 01Why a custom MCP over a generic connector

    Generic API connectors leak schema noise into the model context — every field of every response, none labeled. A purpose-built MCP server lets you pick exactly the right slice for the questions people actually ask, name them in human language, and discard the rest. The signal-to-noise jump is the whole point.

  2. 02Errors as a feature, not a failure mode

    When BambooHR returns 4xx/5xx, the server translates them into MCP error responses with “what to try next” hints. Claude then recovers in conversation instead of silently dropping the task — a small change with a big impact on perceived reliability.

Outcome

Runnable in any Claude Desktop install with a single config line. Useful as a working example of what a senior, opinionated MCP server looks like — strict types, narrow tool surface, no creepy mutation paths.

Stack

  • TypeScript
  • Node.js 20
  • @modelcontextprotocol/sdk
  • Zod
  • BambooHR REST API

Working on something similar?

Free 15-minute discovery call. We’ll work out whether I’m the right person for it, and if not, I’ll point you somewhere better.

Next case study

Sakan Holiday

B2C short-term rental marketplace built and scaled over 3.5 years. Multi-country, multi-currency, multi-payment-gateway.

Read the case study
BambooHR MCP Server · Alex Thomas