Giving AI Agents a Design System

aidesign-systemsbuilding-in-public

Last month I wrote about the gap between vibe coding and engineering, how AI agents can write code but don’t understand your system. The design system was one of the examples: Copilot hallucinates component names because Sirius2 isn’t in its training data. Agents suggest ERB syntax when we use ORB. They guess at prop names and get them wrong.

Today we fixed that, at least for the design system. Sirius2 now has an MCP server.

What the MCP server exposes

MCP (Model Context Protocol) lets AI agents discover and use tools programmatically. Our Sirius2 MCP server exposes:

When an AI agent connects to the MCP server, it can query “what Button variants exist?” and get back a structured response with every variant, every tone, every prop, and usage examples in ORB syntax. It doesn’t need to guess or hallucinate. The design system tells it exactly what’s available.

Why self-documenting components made this possible

This is the payoff of a decision we made almost two years ago. Every Sirius2 component carries its own documentation as a Ruby metadata class: structured data with props, slots, examples, and notes. The documentation app reads these classes to render browsable pages. The MCP server reads the same classes to respond to tool calls.

If the documentation had lived in markdown files, building the MCP server would have meant parsing prose into structured data, an unreliable and brittle process. Because the docs are already structured Ruby objects, the MCP tools are thin wrappers around existing methods. find_components calls the same code that renders the component index. get_component_details returns the same metadata hash that the documentation page uses.

The struggle

Adding MCP to Sirius2 was harder than I expected. The first implementation used raw JSON-RPC over SSE and STDIO, implementing the protocol manually. It worked, sort of. Then we hit edge cases: the MCP client reported invalid array slots, metadata wasn’t grouped correctly, the SSE connection dropped under load.

The frustration of building this, all the protocol plumbing, the connection management, the serialization quirks, is making me think: why is adding MCP to anything so hard? The protocol is standardized. The tools pattern is well-defined. But every implementation requires the same boilerplate: set up a transport, handle the handshake, dispatch tool calls, serialize responses.

There should be a way to just point at a folder of tools and say “serve these as MCP.” Like Vercel for MCP servers.

Early results

Even with the rough edges, the MCP server is already changing how we work. Before it, asking an agent to build a settings page with Sirius2 components was an exercise in correction. “No, the prop is called variant, not type.” “No, Card:Section uses a colon, not a slash.” “No, that component doesn’t exist.” Every interaction started with the agent guessing and me fixing.

With the MCP server, the agent queries the design system first. “What components exist in the ‘layout’ group?” → Layout, BlockStack, InlineStack, InlineGrid, Grid, Box, FormLayout. “What props does Card accept?” → title, sectioned, subdued, plus the Section and Footer slots. The agent writes ORB templates that use the correct components with the correct props because it’s working from the actual API, not from training data.

This is what I meant in December about AI needing scaffolding, not smarter models. The model didn’t get better. We gave it access to the right information. The MCP server is that bridge.

The implementation is rough and I already know we’ll need to rebuild it. But the concept is proven. The investment in semantic tokens, component documentation, and a coherent design philosophy wasn’t just for human developers. It’s for AI agents too. The clearer and more structured your system, the better AI can work with it.