{"slug": "your-design-system-needs-an-mcp-server", "title": "Your Design System Needs An MCP Server", "summary": "A developer has proposed that design system teams build a Model Context Protocol (MCP) server to give AI coding agents accurate, structured access to component documentation and design tokens. Without an MCP server, AI tools like GitHub Copilot and Claude often guess how internal components work by scanning `node_modules` folders, leading to hallucinated APIs and incorrect code. The MCP server exposes \"tools\" that AI agents can invoke to retrieve precise component specifications, replacing guesswork with reliable context and reducing wasted tokens.", "body_md": "One of the best investments you can make for your design system right now is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/docs/getting-started/intro) server. As AI models evolve they are becoming increasingly more capable, but using them effectively comes at a cost. Every token an AI wastes guessing how your components work is money left on the table. An MCP server gives an AI a set of tools it can use to look up exactly how to use your design system. Think of it like an API that an AI can call instead of guessing. In this article we'll talk about why this matters and how you can get started with your own.\n\nBefore we get into it, there are some things about AI usage worth addressing. I've had my fair share of scepticism in the past, but recent model releases have made it increasingly difficult to argue that AI isn't a viable tool for the majority of workstreams, including building user interfaces. Most large language models are trained on public data scraped from the internet, which means your internal design system is largely invisible to them, especially if your documentation lives behind a corporate network. Without an MCP, an AI agent in any agentic coding tool such as [GitHub Copilot](https://github.com/features/copilot) or [Claude](https://claude.ai/) will still try to use your system, often by poking around your `node_modules`\n\nfolder, filling in the gaps with its best guess. That’s where hallucinated component APIs come from. An MCP server doesn’t just make AI more useful for your design system, it directly addresses the trust problem by replacing guesswork with accurate, structured context.\n\nAt the end of the day, users of a design system want a UI that adheres to brand guidelines as fast as possible. So why not empower them to use AI more effectively by giving their agents a recipe book for your system? It's a win-win: they get a layout that uses the right components and design tokens from the start, and you get more adoption without the usual hand-holding. An MCP server makes that possible by giving AI agents concrete tools to resolve those questions, producing code samples and design token usage that actually reflect your intent rather than an educated guess.\n\nGetting started with an MCP server is more straightforward than you might expect. Rather than endpoints, an MCP server exposes \"tools\" that an AI can invoke by name when it needs information. The key difference from a traditional API is that the response doesn't need to conform to a strict schema. Unlike a REST API where a client breaks if the shape of the data changes, an MCP tool can return loosely structured text or JSON and the AI will make sense of it. Your job is simply to point it toward the right information.\n\nIf you're using Node you can use the [@modelcontextprotocol/sdk](https://www.npmjs.com/package/@modelcontextprotocol/sdk) package to get started:\n\n``` js\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\n\nconst server = new McpServer({\n  name: \"my-design-system\",\n  version: \"1.0.0\",\n});\n\n// Tools are registered here - see examples below\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n```\n\nFor a design system, you'll want to consider exposing tools along these lines:\n\nThis gives an AI the ability to query your entire design system and drill down into specific component details as needed. How you build these tools will depend on your existing setup. [If you're primarily building Web Components](https://jamesiv.es/blog/frontend/javascript/2024/03/26/demystifying-web-components/), you can connect directly to your Custom Elements Manifest, which already includes all of your components and their options in a machine-readable format:\n\n``` python\nimport fs from \"fs\";\nimport { z } from \"zod\";\n\ninterface CustomElementDeclaration {\n  kind: string;\n  name: string;\n  tagName?: string;\n  summary?: string;\n  customElement?: boolean;\n  members?: Array<{ kind: string; name: string; type?: { text: string }; description?: string }>;\n  slots?: Array<{ name: string; description?: string }>;\n  events?: Array<{ name: string; description?: string }>;\n}\n\ninterface CustomElementsManifest {\n  modules: Array<{\n    declarations?: CustomElementDeclaration[];\n  }>;\n}\n\nconst manifest: CustomElementsManifest = JSON.parse(\n  fs.readFileSync(\"./custom-elements.json\", \"utf-8\")\n);\n\nconst getDeclarations = (): CustomElementDeclaration[] =>\n  manifest.modules.flatMap((m) => m.declarations ?? []);\n\nserver.tool(\n  \"list-components\",\n  \"Lists all available components in the design system\",\n  {},\n  async () => {\n    const components = getDeclarations()\n      .filter((d) => d.kind === \"class\" && d.customElement)\n      .map((d) => ({ name: d.name, tagName: d.tagName, summary: d.summary }));\n\n    return {\n      content: [{ type: \"text\", text: JSON.stringify(components, null, 2) }],\n    };\n  }\n);\n\nserver.tool(\n  \"get-component\",\n  \"Gets the full details of a component including its properties, slots, and events\",\n  { name: z.string().describe(\"The component class name or tag name\") },\n  async ({ name }) => {\n    const component = getDeclarations().find(\n      (d) => d.name === name || d.tagName === name\n    );\n\n    if (!component) {\n      return {\n        content: [{ type: \"text\", text: `Component \"${name}\" not found.` }],\n      };\n    }\n\n    return {\n      content: [{ type: \"text\", text: JSON.stringify(component, null, 2) }],\n    };\n  }\n);\n```\n\nIf you're using Storybook, you can walk the Abstract Syntax Tree to reassemble stories as queryable examples. [I wrote about this in another article in the context of validating server-side rendering](https://jamesiv.es/blog/frontend/javascript/2026/03/13/headless-storybook-with-lit/), but the same principles apply here. Alternatively you could use the [Storybook MCP server](https://storybook.js.org/docs/ai/mcp/overview) plugin to expose your stories as tools without needing to write any custom code if you're using a language it supports.\n\n``` python\nimport ts from \"typescript\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { glob } from \"glob\";\n\ninterface Story {\n  name: string;\n  source: string;\n}\n\nfunction extractStories(filePath: string): Story[] {\n  const code = fs.readFileSync(filePath, \"utf-8\");\n  const sourceFile = ts.createSourceFile(\n    filePath,\n    code,\n    ts.ScriptTarget.Latest,\n    true\n  );\n\n  const stories: Story[] = [];\n\n  ts.forEachChild(sourceFile, (node) => {\n    if (\n      ts.isExportDeclaration(node) ||\n      (ts.isVariableStatement(node) &&\n        node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword))\n    ) {\n      if (ts.isVariableStatement(node)) {\n        for (const decl of node.declarationList.declarations) {\n          if (decl.initializer && ts.isObjectLiteralExpression(decl.initializer)) {\n            stories.push({\n              name: decl.name.getText(sourceFile),\n              source: decl.initializer.getText(sourceFile),\n            });\n          }\n        }\n      }\n    }\n  });\n\n  return stories;\n}\n\nserver.tool(\n  \"get-component-examples\",\n  \"Gets Storybook usage examples for a given component\",\n  { name: z.string().describe(\"The component name to find stories for\") },\n  async ({ name }) => {\n    const files = await glob(`**/${name}.stories.{ts,tsx}`, {\n      ignore: \"node_modules/**\",\n    });\n\n    if (!files.length) {\n      return {\n        content: [{ type: \"text\", text: `No stories found for \"${name}\".` }],\n      };\n    }\n\n    const stories = extractStories(files[0]);\n\n    return {\n      content: [{ type: \"text\", text: JSON.stringify(stories, null, 2) }],\n    };\n  }\n);\n```\n\nYour server can be consumed by most agentic coding tools, though the configuration format varies depending on which one you're using. For VS Code, add a `.vscode/mcp.json`\n\nfile and point it to either the HTTP address or stdio path:\n\n```\n{\n  \"servers\": {\n    \"my-design-system-mcp\": {\n      \"type\": \"http\",\n      \"url\": \"https://your-design-system.com/mcp\"\n    },\n    \"my-other-mcp\": {\n      \"command\": \"node\",\n      \"args\": [\"./mcp/server.js\"]\n    }\n  }\n}\n```\n\nOnce you have an MCP server running, your documentation becomes even more important since it's now being read by both humans and AI agents. This is a good opportunity to get specific. Rather than documenting that a Button component accepts a `variant`\n\nprop, document why certain combinations should be avoided - for instance, `variant=\"ghost\"`\n\nis intentionally low-contrast and shouldn't be used as a primary call to action inside a form. An AI without that context will reach for it anyway because it looks like a valid option. The same applies to workarounds that have been passed around as word of mouth — if it lives only in someone's head or a Slack thread, an AI will never find it. The more focussed and opinionated your documentation is, the more focussed and opinionated your AI's output will be. Refactors that would otherwise take weeks can realistically come down to days or hours if the context is solid.\n\nIf you want to unlock even more potential, [pairing your MCP server with Figma Code Connect](https://jamesiv.es/blog/frontend/javascript/2025/09/21/custom-elements-manifest-and-figma-code-connect/) is worth the investment. [Figma has its own MCP server](https://help.figma.com/hc/en-us/articles/32132100833559-Guide-to-the-Figma-MCP-server) that can read design specs directly. Since you can connect multiple MCP servers at once, an AI can take a design spec, identify the right components to use, and then query your design system MCP for the implementation details and edge cases. The result is a workflow where AI moves from design to code with much less back and forth.\n\nThe teams that will get the most out of AI tooling are the ones who give it the best context to work with. A well-maintained design system with an MCP server is exactly that: a direct line between your standards and the tools your engineers use every day. The abstraction and consistency that design systems have always provided become significantly more valuable when AI can consume them reliably. This applies beyond design systems too, any well-documented library benefits from the same approach.\n\nDesign systems have always been about giving teams a shared language. The MCP server just means AI gets to speak it too.", "url": "https://wpnews.pro/news/your-design-system-needs-an-mcp-server", "canonical_source": "https://dev.to/jamesives/your-design-system-needs-an-mcp-server-4c7a", "published_at": "2026-05-26 13:47:50+00:00", "updated_at": "2026-05-26 14:03:51.631563+00:00", "lang": "en", "topics": ["ai-tools", "ai-agents", "large-language-models", "artificial-intelligence", "generative-ai"], "entities": ["Model Context Protocol", "GitHub Copilot", "Claude"], "alternates": {"html": "https://wpnews.pro/news/your-design-system-needs-an-mcp-server", "markdown": "https://wpnews.pro/news/your-design-system-needs-an-mcp-server.md", "text": "https://wpnews.pro/news/your-design-system-needs-an-mcp-server.txt", "jsonld": "https://wpnews.pro/news/your-design-system-needs-an-mcp-server.jsonld"}}