---
title: "Pine MCP: A Case Study"
date: 2026-10-13
author: "Phillip Lovelace"
---

# Pine MCP: A Case Study

Pine MCP is a server that lets AI coding tools read Pine, Kajabi’s design system. Instead of guessing at components, an agent can look up the real ones, get Pine’s rules before it starts and check what it wrote against them.

I started it in June 2025 and co-maintain it with a teammate. The biggest surprise? AI agents are a new kind of design system user with limits of their own, and most of the hard problems turned out to be documentation problems.

RoleSr. UX Developer, co-maintainer

CompanyKajabi · 2020 to present

On Pine MCP2025 to present

FocusLookup, response budgets, testing and upkeep

FrameworkNext.js

ProtocolMCP, via the official SDK

- **15** MCP tools
- **3** tool groups
- **5+** AI clients
- **64–99%** smaller replies



## The problem

If you’ve pointed an AI coding tool at a design system, you’ve probably seen this. In 2025, the tools writing UI for Kajabi kept inventing Pine components and props that didn’t exist. The code looked right. It wasn’t. Pine’s docs were written for people, as pages you browse, and an agent can’t query those reliably. So it filled the gaps with whatever was most common on the public web.

MCP (Model Context Protocol) is a standard way for AI tools to call outside services. We looked at other options first. Rules pasted into each tool’s prompt drift out of date. An instructions file in the repo helps, and we kept one, but it can’t answer questions. A custom API would have worked for nobody until every tool learned to call it. MCP won because the tools already speak it, which made it an easy call. (For Pine itself, see the [Pine case study](/portfolio/pine-design-system).)

## How it works

At build time the server reads from three sources: Pine’s repo for component source, docs and page patterns, plus the design tokens and the icon catalog. A data build fetches everything, checks that nothing is partial or empty and writes a static snapshot. The server is one stateless endpoint, and every tool answers from that snapshot.

Under the hood it’s a small Next.js app, with the official MCP SDK handling the protocol itself. That leaves almost all of our own code about Pine: what to return, in what order and how much.

 How the Pine MCP server works Three sources (the Pine repo with component source, docs and patterns; design tokens; and icons) feed a data build that runs on every deploy. The build fetches everything, checks that nothing is partial or empty, and writes a static snapshot. If any check fails, the deploy stops and the last good version stays live. The snapshot is bundled into the Pine MCP server, a single stateless endpoint with a size budget on every response, offering three groups of tools: look up, check, and guide. AI coding tools, an edit hook and review agents call those tools, and the result is product UI built from real Pine components.         SOURCES, READ AT BUILD TIME  Pine repo components, docs, patterns  Design tokens core and semantic tokens  Icons icon catalog   fetched in batches, with retries   Data build, on every deploy  fetch everything   check: nothing partial or empty   write a static snapshot If any check fails, the deploy stops and the last good version stays live.  bundled with the server  Pine MCP one stateless endpoint · a size budget on every response  Look up components, tokens, icons, docs, patterns  Check generated code against three tiers of rules  Guide Pine's rules before an agent writes code   MCP tool calls   AI coding tools Claude Code, Cursor, Windsurf, VS Code  Edit hook checks Pine markup after each AI edit  Review agents design review on pull requests  write code  flag issues  review  Product UI built from real Pine components, checked against Pine's rules 

Data is fixed at deploy time, and a bad build never replaces a good one.Fixing the data at deploy time keeps answers fast and predictable, and an outage upstream can’t break a request. The cost is freshness, so a scheduled check compares the snapshot with Pine and flags drift. The tools fall into three groups:

### Look up

Components, tokens, icons, docs and page patterns, found by name even when an agent’s guess is a little off.



### Check

Reviews generated code against three tiers of Pine’s rules, and every problem it finds comes back with a fix.



### Guide

Agents get Pine’s rules before they write anything, plus a quick answer on whether a request involves Pine at all.





The callers are AI coding tools like Claude Code, Cursor, Windsurf, VS Code and ChatGPT, a hook in the product repo that checks Pine markup after each AI edit, and design review agents on pull requests.

## Agents read through a keyhole

In early 2026, Claude Code, Cursor and Windsurf started failing on some calls. The responses were too big for them to take in. My note on the fix didn’t sugarcoat it: “the verbose defaults were the problem.”

So every response got a size budget. The most important part of each answer comes first, so a component’s API is never the part that gets cut. Agents can ask for specific sections, fewer examples or the next page, and when an answer hits its budget, it says how to get the rest.

 How much smaller responses got after adding size budgets Bar chart. Each bar shows how much smaller a response got after size budgets were added in March 2026, out of 100%. Icon list: 99% smaller. All tokens: 97% smaller. Table component: 93% smaller. Button component: 70% smaller. Box component: 69% smaller. Input component: 64% smaller.    Response sizes after adding budgets How much smaller each response got, out of 100% Icon list  Icon list: 99% smaller 99% smaller All tokens  All tokens: 97% smaller 97% smaller Table component  Table component: 93% smaller 93% smaller Button component  Button component: 70% smaller 70% smaller Box component  Box component: 69% smaller 69% smaller Input component  Input component: 64% smaller 64% smaller Measured when budgets were added in March 2026. Some components grew again later, when every code example was restored to their responses. 

Budgets cut responses by 64–99%, with the biggest ones shrinking the most.Honestly, this was a documentation problem the whole time. What does a short description of a table component need to say first? Budgets also need tending. When we later put every code example back into component responses, some grew again, and I still think that was the right call.

## Names are fuzzy

Agents ask for `btn`, `radiogroup`, `pds-button` or `Button`, and all of them should land on the right component. The obvious tool is edit distance, the number of single-letter changes between two words. By that measure, though, `btn` is closer to `box` than to `button`. Oops.

So lookup works in order: a short list of known aliases, then an exact match that ignores the `pds-` prefix, then a partial match, then edit distance with tight limits that only accepts one clear winner. If nothing wins, the agent gets “did you mean” suggestions. I’ll take a question over a confident wrong answer every time.

## Healthy and serving nothing

In spring 2026, a credential problem meant the data build couldn’t read its sources. The deploy still succeeded and shipped empty data. In my own words at the time, the server was “healthy” and serving nothing. Not a great look for a tool whose whole job is accurate answers.

Now the build fails closed. It refuses to ship a partial component set or empty data, never deletes good data before new data arrives and leaves the last good version live when anything fails. The same work added the server’s first automated tests. They run without network access, so upstream Pine changes can’t break them, and I broke the code on purpose to prove they’d catch it. Now hundreds of tests run on every pull request.

## My contributions

I started Pine MCP and co-maintain it with a teammate. They built the tools that check generated code and guide agents before they write, along with the logging that shows how the server gets used. We made most of the big calls together. These are the parts where I’ve had the biggest hand.

### First versions

Built the first version in June 2025, six tools in one serverless function, then rebuilt it a week later as the Next.js app it still is.



### Lookup

Token and icon lookup, plus the forgiving name matching that sends near-miss names to the right component, or asks.



### Response budgets

Size budgets on every response, the most important part first, and ways for agents to ask for more or less.



### Data build

The build that fails closed, so partial or empty data never ships and the last good version always stays live.



### Tests and CI

The server’s first automated tests and CI, run offline on every pull request and proven by breaking code on purpose.



### Security and upkeep

Patching a published SDK vulnerability, moving to MCP SDK version 2 and refactoring with output unchanged.





![Pine MCP documentation page](/wp-content/themes/pixelflipsv3/images/case-studies/pine-docs-mcp.webp)Pine MCP’s page in the Pine docs, next to the components it serves.## Outcomes

### Where code gets written

Registered project-wide in Kajabi’s main product repo, so AI tools working in that codebase use it.



### Answers that fit

Responses are 64–99% smaller, with the important part first and the rest a request away.



### Steady use

Thousands of requests a month from engineers’ AI tools, the edit hook and review agents.





## What I learned

### Build the guardrails first

Size budgets, tests and the fail-closed build each showed up only after something broke. Next time they come first, before anyone depends on the output.



### Agent-facing text is product copy

Tool descriptions and error messages are the interface an agent reads. They deserve the same care as a component’s docs, and they go stale the same way.



### A checker has to be right

A rule that flags correct code teaches people and agents to ignore it. How often a checker is wrong matters more than how much it checks, so rules get tested against Pine’s own examples.



### Refactors need proof

When other people’s agents depend on your exact output, confidence isn’t enough. I compared every response before and after a refactor, byte for byte, and shipped only when they all matched.