Documentation
Create and share your design systems.
Platform stores your design system as a JSON token document per project. You author it visually, share it with teammates, and read it from AI tools through the built-in MCP server, with no export step in between.
How it works
Platform has one core idea: your tokens live in one place, and everything else reads from it. The loop looks like this.
- Create a project. Each project is a single JSON token document. New projects start from a small default palette you can replace immediately.
- Author your tokens. Organize them into groups: color, typography, spacing, radius, shadow, or anything your team invents. Color values render a live swatch as you type.
- Share the project. Invite teammates by email. Shared projects show up in their workspace and they can edit alongside you.
- Consume the tokens anywhere. Export for code, copy CSS custom properties, call the REST API, or connect an AI coding agent over MCP. All four read the same document, so nothing drifts.
Everything is scoped to your account. You always see the projects you own plus the ones shared with you, and never anything else.
Projects & tokens
A project holds token groups: color, typography,
spacing, radius, shadow, or any group
you add. Each group declares a type, an optional
description, and an array of tokens:
The group's type tells the editor how to preview each value: a
color chip, a rounded corner, the real shadow, which is why it is declared
rather than guessed: 16px is equally valid as spacing, a radius,
a border width or a font size, and nothing about the value itself says which.
It is set when the group is created and fixed from then on, so every token in
a group shares it.
basic: anything with no visual formfontFamily: font stacksfontSize: font sizes, a type scalefontWeight: font weightslineHeight: leadingcolor: a single colorgradient: linear, radial and conic gradientsopacity: a number from 0 to 1borderRadius: corner radiiborderWidth: border and stroke widthsboxShadow: box shadowsclipPath: clip pathssize: padding, margin, gap, width, heightletterSpacing: trackingtextShadow: text shadowsimage: image URLssvg: inline SVG markup or an .svg pathimageMask: mask imagesfilter: blur, brightness, drop-shadow…transform: translate, scale, rotate…
A token has just a name and a value: anything
explanatory belongs to the group, not to each value. Values are free-form
strings, so a token can hold a hex color, a length, a font stack, or a full
shadow definition.
basic is the exception: it is the one type with nothing to preview,
so each of its tokens may carry a description, shown in the column
the swatch would otherwise occupy.
The editor
The workspace has three panes: token groups on the left, the tokens in the selected group in the middle, and the AI agent on the right.
- Add a group with + New group in the left sidebar.
- Add a token with + Add token below the list.
- Save with the Save button or Cmd/Ctrl + S. A dot beside the project name marks unsaved changes.
- Copy CSS puts the whole set on your clipboard as custom properties. See Importing & exporting for the other two formats.
Every variable is named --type-name. The type is the
namespace, so --color-blue-500 is known to be a color and
--size-gutter a length without reading anything else. A group's
name is a label for you and never appears in a variable, which is why two
groups of the same type cannot both hold a token called
500.
Rules
Tokens are values; Rules is where you say what they're for: which green is for destructive actions, when to reach for the large radius, what never to touch the display face. It's a page like Preview, always there below your token groups, written as plain Markdown text with syntax highlighting to make the structure visible as you type. Nothing renders it as HTML anywhere, so there's nothing to sanitize and nothing to style beyond the editor itself.
Rules travel differently from everything else in the project. They're sent with MCP reads, ahead of the tokens themselves, since an agent writing code needs to know which token to reach for before it needs the list of values, and they're kept in the backup file. They are never included in the CSS, code or Figma exports: only you and your collaborators, and whatever you connect over MCP, ever see them.
One exception, and it is on purpose: a duplicate inherits them. Duplicating is one person deciding to take your system and use it, so the copy arrives with the reasoning as well as the values, which is the half that cannot be worked out by reading the tokens. Write them for that reader too.
Recording a licence
A system that points at a typeface or a photograph carries a question with it: who may use this, and where? Answering it in the project is worth the two minutes, and where you answer it decides who ever sees the answer.
-
A basic token group: call it
licences: travels everywhere the tokens do: into the CSS, code and Figma exports, out over MCP, and into any copy somebody makes of your system. The basic type has no swatch and shows a description instead of a preview, which makes it the one type built for text a reader needs rather than a value a build consumes. One token per asset, the licence URL as its value, the terms in a sentence as its description. - Rules stays out of every export, but it is copied when somebody duplicates your project, so it reaches the person most likely to need it, and nobody who is only reading. Put the explanation here and the pointer in a token, and both audiences are covered.
And the simplest answer of all is to pick assets that need no explanation. Anything under the SIL Open Font License, most of Google Fonts, can be exported and copied by whoever you share your system with, which is what a design system is for. See the terms for what you are agreeing to when you upload or link an asset.
Importing & exporting
A system you already have does not need retyping, and one you build here does not need to stay here. Both directions are deterministic: the same file produces the same tokens every time, with no AI in the path. The agent is for judgement (naming a scale, filling a gap); moving a file is not judgement, so nothing here guesses.
Importing
In an empty project, press Import. In a project that already has groups, use Import tokens in the project menu. Paste the text or choose a file. The format is worked out for you, so there is nothing to pick.
Importing replaces every token in the project. It is meant for the first move of a system into Platform, not for merging two. The dialog says so, and names the number of groups it is about to replace.
- CSS custom properties: paste a stylesheet, or the
:rootblock out of one. Declared more than once, the last value wins, the way the cascade would read it. - DTCG (
$value/$type): the Design Tokens Community Group format, and the one to prefer. See below. - Style Dictionary and Tokens Studio
(
value/type). - Flat JSON:
{"color-primary": "#4ED391"}, which is what smaller exporters write. - Platform's own backup file (Export backup), so a system can be moved between projects or edited by hand and put back.
Afterwards you are told how many tokens landed in how many groups, and how many could not be typed. That last number is the one worth reading: anything unrecognisable goes to a group called Other rather than being dropped or guessed at, so it stays visible and is one drag from the right place.
Exporting
Four formats, for four different readers, all in the project menu. Three of
them write JSON, so each is named for where it goes rather than for its
format: Export for code and Export backup are both
.json files and are not interchangeable.
- Copy CSS: custom properties for a stylesheet.
- Export for code: a flat
.jsonof camelCased keys, for anything that reads styles as JavaScript objects: React Native, styled-components, a theme file. - Export backup: Platform's own document, tokens and Rules together. For backups and for moving a system between projects; it is the only export that imports straight back and the only one that carries your rules. The other three are token-only. No design tool reads this one.
- Export to Figma: a DTCG token file, which is what design-tool plugins read.
Using tokens in JavaScript
Export for code gives you one flat object, keyed by the same
js_property name the MCP server reports, so an agent and a build
script see one convention rather than two:
It is .json rather than .js deliberately: every
JavaScript environment imports JSON, so there is no module format to choose
and no TypeScript to require of anyone.
Values keep their units. "16px", not
16. That is lossless and directly correct for the web; React
Native's dimension props want numbers, so a project there applies
parseFloat to the handful that need it. Stripping units here
would be guessing which consumer is reading the file.
Colors, font stacks and shadows cross untouched: modern
React Native takes a CSS shadow string in boxShadow verbatim,
which makes a shadow the one token that is identical in Figma, CSS and React
Native at the same time.
Two types are rewritten: filter and
transform. React Native takes a list of one-key
objects for both, not a string, so the CSS text does nothing there at
all: brightness(120%) blur(4px) comes out as
[{ brightness: 1.2 }, { blur: 4 }]. Percentages become
ratios, lengths lose their unit, and angles stay text
({ rotate: "12deg" }). A chain holding something the array
form cannot say (drop-shadow(), matrix(), a
3d function) keeps its CSS string whole instead, so you can see what
was meant rather than half a value that looks applied.
One value does not translate: lineHeight. CSS
reads 1.5 as a multiplier; React Native reads it as 1.5
pixels. Converting needs the font size, which a line-height token
does not know. It is exported anyway, because it is correct for CSS and every
web consumer, but multiply it yourself before handing it to React Native.
If two token names differ only in hyphenation (green-500 and
green500), they camelCase to the same key and one replaces the
other. Platform counts what went in against what came out and tells you when
that happens; rename one of them.
The Figma round trip
Figma has no built-in token export, so this needs a plugin. We suggest DTCG Design Token Manager, because it goes both ways: it can read a token file into Figma variables and write your variables back out. A one-directional plugin only solves half of this.
Figma → Platform. Export your variables from the plugin as DTCG, then paste the file into Platform's importer, or choose it with the file button.
Platform → Figma. Use Export to Figma, then import the file with the same plugin. Everything is written as a literal value, never a reference, because that is what variable plugins can reliably read.
We favour DTCG over the alternatives for one reason: it is typed.
The file states that 4 is a dimension and #4ED391 is a
color, so almost nothing has to be inferred. A stylesheet, by contrast, gives
us 4px and leaves us to work out whether that is a radius, a gap
or a border width, a guess we make from the name, and get wrong sometimes.
Naming your Figma variables tier/type/name is what makes the trip
clean in both directions. primitives/color/green/500 becomes a
token called green-500 in a group called Primitives Color,
and exports as --color-green-500. The type word is dropped from the
name because Platform puts it back; leaving it in would give you
--color-color-green-500.
Words that name a type: color, gradient,
opacity, radius, border-width,
shadow, spacing, font-family,
font-size, font-weight, line-height,
letter-spacing. One to avoid: border on its own means
border width, so a group of border colors called border
will preview as lines rather than swatches. Call it something else.
Figma writes lengths as plain numbers with the unit implied, so a spacing of
4 arrives without its px. Platform puts it back,
since pixels are what Figma counts in and it has no way to say otherwise.
What Figma cannot carry
Figma variables come in four kinds: color, number, string and boolean, and that is the whole of what round-trips. In practice it covers colors, spacing, radii, border widths, type sizes, weights, line heights, letter spacing and font families, which is most of a design system.
The rest has to be added in Platform directly:
- Shadows and gradients are Figma styles, not variables. A variables plugin will skip them, and they will not come back out. Platform still exports them, so nothing is lost on our side.
- Images, SVG, clip paths, image masks, filters and transforms have no Figma variable at all. Add a logo or an icon by pasting its markup or its URL into a token here.
This is a limit of Figma's data model rather than of the format, and no plugin gets round it. Treat Platform as the source of truth for the whole system, and Figma as the place the variable-shaped part of it is designed.
Sharing with your team
Open a project and use Share to invite a teammate by the email they registered with. You can invite someone before they have an account, and the project is waiting when they sign up. Shared projects appear in their workspace with a shared badge.
Each person you invite gets a role, which you can change or revoke at any time from the same dialog:
- Viewer: can open the project, read and export its tokens, copy the CSS, and duplicate it into a project of their own. The editor is read-only for them.
- Editor: everything a viewer can do, plus changing tokens and saving.
MCP follows the same rules: a viewer's token gives read access through
list_projects and get_design_tokens, which is
usually all a developer consuming the system needs. Deleting and
managing access stay with the owner.
AI agent
The right-hand panel is an assistant that knows your current tokens. Ask it to build a palette, extend a type scale, or check a set for gaps. When it proposes changes it returns a complete token document, and the editor applies it as soon as it arrives, as swatches and values rather than as JSON to read. Nothing is saved by this: the project is marked unsaved, so Save and Revert both still mean what they meant. The editor is locked while the agent is working, so a click on New group mid-answer cannot land on a project that is about to change underneath it.
Use the Generate button in the toolbar to hide or show the
panel; your conversation is kept either way. The agent requires
OPENAI_API_KEY on the server, and tells you if it isn't configured.
MCP server
Platform exposes a Model Context Protocol server at
https://platformdesign.app/mcp over Streamable HTTP. Any MCP
client can connect and read your design tokens as live context, so the
agent writing your components uses your real values instead of guessing.
It is a remote HTTP server, not a local one: there is nothing to install to connect to it, and no npm package in the loop. A client needs only the URL above and your access token.
1. Get your access token
Open the app and click Copy MCP token in the top bar. That copies the same JWT the API uses. You can also get one from the command line:
Tokens expire after 72 hours. When a client starts returning authorization errors, copy a fresh one.
2. Connect a client
Claude Code: add the server from your terminal:
Cursor, Claude Desktop, or any client using a JSON config: add an entry under mcpServers:
Restart the client after editing its configuration. If you are running
Platform yourself, substitute your own host. Locally that is
http://localhost:8080/mcp.
A few older clients only speak the local stdio transport and cannot
connect to a remote server directly. Those need a stdio-to-HTTP bridge such
as mcp-remote; every current client listed above connects
natively.
3. Available tools
-
list_projects: every project you own or that is shared with you, with its id, name, description and last update. -
get_design_tokens: the full token document for one project, plus its Rules, the author's own notes on how the system is meant to be used, read before the tokens themselves. Takesproject_id, which you get fromlist_projects.
4. Verify the connection
In Claude Code, run /mcp to confirm platform is
connected. Then ask the agent something that needs your tokens, "what are
my color tokens?", and it should call the tools and answer with real
values. You can also check the endpoint directly:
A 401 means the token is missing or expired. A successful call
returns the tools above.
5. Add the Agent Skill
The MCP hands an agent your design system. The Agent Skill teaches it what to do with one: read your Rules before the values, use each token's exact exported name rather than a name it invents, and tell you when your system has no token for something instead of quietly hardcoding it. Installing it is one command:
Restart your client afterwards so it picks the skill up. It is optional — the server works without it — but it is the difference between an agent that can read your tokens and one that reliably does. The skill is open source, so you can read exactly what it tells your agent to do at github.com/Platform-DS/platform-design-skills.
6. Sync your tokens
MCP doesn't push updates: a connected client only reads your tokens when you ask it to. There's no background sync and no ID to remember: just refer to your project by the name shown in the app, and the agent looks up its id for you.
Re-run this any time your tokens change on Platform. Nothing updates your code automatically. If you have two projects with the same name, the agent will ask which one you mean.
REST API
Every endpoint takes an Authorization: Bearer <token>
header, using the same token as MCP.
POST /api/account/register:{name, email, password}POST /api/account/authenticate:{email, password}GET /api/projects: list your projects (owned + shared)POST /api/projects: create{name, description, tokens}GET /api/projects/{id}: read one projectPUT /api/projects/{id}: update{name, description, tokens, rules}(owner or editor);rulesis optional and leaves existing rules alone if omittedDELETE /api/projects/{id}: delete (owner only)POST /api/projects/{id}/share: share{email, role}(owner only)GET /api/projects/{id}/shares: list collaborators (owner only)PATCH /api/projects/{id}/shares: change a role{email, role}(owner only)DELETE /api/projects/{id}/shares: revoke{email}(owner only)
Self-hosting
Platform is self-hostable and ships with everything needed to run it
yourself. See the repository README for the current setup steps. The
server needs DATABASE_URL and JWT_SECRET, plus
OPENAI_API_KEY if you want the agent sidebar.