Skip to main content
Glama

perfectui-mcp

A read-only MCP server that gives coding agents the real classes, markup and install instructions of Perfect UI 1.0.0, so they stop inventing pui-* classes.

Everything the server answers comes from one file, data/corpus-1.0.0.json, generated from the library's documents at the v1.0.0 tag and from the stylesheet of the published @chrissgon/perfectui@1.0.0 package. The server never writes, runs or downloads anything, and it makes no network requests.

The npm package is @chrissgon/perfectui-mcp.

Tools

Tool

Input

Returns

get_install

none

The npm command and the other package managers' commands, the CDN stylesheet and script URLs, and the import statements, all pinned to 1.0.0 (never latest).

list_components

none

Every document in the documentation's order (components, forms, layout, customization, guides): slug, title, section and a one-line description.

get_component

name: a slug from list_components, for example button

The document's full Markdown, its HTML examples and the pui-* classes they use.

search_docs

query (1 to 200 characters), limit (1 to 10, default 5)

The best matching sections: slug, heading, snippet and score. Prefix matching and one typo from four characters.

check_markup

html (up to 100,000 characters)

Every pui-* class in class or className attributes that Perfect UI 1.0.0 does not define (kind: "unknown"), with its line and the closest real class; and every Perfect UI 0.23.0 class that the migration guide renames (kind: "legacy"), with the guide's replacement.

Every tool declares readOnlyHint: true, destructiveHint: false, idempotentHint: true and openWorldHint: false, rejects unknown arguments, and returns structured content that matches its output schema.

A typical agent loop: list_components or search_docs to find the right document, get_component to copy its markup, then check_markup on the result before handing it over.

check_markup { "html": "<button class=\"pui-button\">Save</button>" }
→ line 1: pui-button → pui-btn (pui-button is not a class of Perfect UI 1.0.0; the Button document (get_component button) uses pui-btn)

Related MCP server: web-ui-component-spec-mcp

Install

Requirements: Node.js 20 or later and npm (the server is developed and tested on Node.js 24).

The server speaks MCP over stdio. An MCP client starts it with npx, which downloads the package on first use:

npx -y @chrissgon/perfectui-mcp

Run on its own, it waits for a client on stdin and prints one status line to stderr.

Client configuration

Many clients take a server as a command and its args inside an mcpServers object like the one below; check where your client keeps it:

{
  "mcpServers": {
    "perfectui": {
      "command": "npx",
      "args": ["-y", "@chrissgon/perfectui-mcp"]
    }
  }
}

Claude Code:

claude mcp add perfectui -- npx -y @chrissgon/perfectui-mcp

VS Code (.vscode/mcp.json in a workspace):

{
  "servers": {
    "perfectui": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@chrissgon/perfectui-mcp"]
    }
  }
}

To pin a version, write it in the package name: @chrissgon/perfectui-mcp@0.2.0.

To try it without a client, use the MCP Inspector's command-line mode. Leave -y out of the server command here: with it, Inspector 2.8.0 fails to start the server (npx still installs the package without asking, because its input is not a terminal).

npx -y @modelcontextprotocol/inspector --cli npx @chrissgon/perfectui-mcp --method tools/list
npx -y @modelcontextprotocol/inspector --cli npx @chrissgon/perfectui-mcp --method tools/call --tool-name get_component --tool-arg name=button

Use it as a library

From 0.2.0 the package also has a library entry, so a host can serve the same five tools over another transport. buildServer(corpus) returns a new McpServer (from @modelcontextprotocol/sdk) that is not connected yet; loadCorpus() reads and validates the bundled corpus. Importing the entry starts nothing. Types ship with the package.

import { buildServer, loadCorpus } from "@chrissgon/perfectui-mcp";
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";

const corpus = loadCorpus(); // once per process

// Stateless Streamable HTTP: a new server and transport for every request.
export async function handle(request: Request): Promise<Response> {
  const server = buildServer(corpus);
  const transport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: undefined, enableJsonResponse: true });
  await server.connect(transport);
  try {
    return await transport.handleRequest(request);
  } finally {
    await transport.close();
    await server.close();
  }
}

The entry also exports TOOL_NAMES, instructions, PACKAGE_VERSION, CORPUS_VERSION and the corpus types. The package reads data/corpus-1.0.0.json and its own package.json from files next to its code, so keep it external when you bundle (installed in node_modules, not inlined). The bin is unchanged: npx -y @chrissgon/perfectui-mcp is still the stdio server.

Run it from a checkout

git clone https://github.com/chrissgon/perfectui-mcp.git
cd perfectui-mcp
npm ci
npm run build

Then point the client at the built file, with your checkout's absolute path:

{
  "mcpServers": {
    "perfectui": {
      "command": "node",
      "args": ["/absolute/path/to/perfectui-mcp/dist/server.js"]
    }
  }
}
npx @modelcontextprotocol/inspector --cli node dist/server.js --method tools/list

Where the data comes from

npm run corpus rebuilds data/corpus-1.0.0.json:

  • Documents: one entry per link in the summary of the library's docs/README.md at the v1.0.0 tag (28 documents, including the migration guide). Each entry keeps the document's Markdown as written, its ```html blocks as examples, and the pui-* classes those examples use.

  • Classes: every .pui-* selector in dist/perfectui.css of the published package (51 classes). The package's npm integrity hash is recorded in the corpus.

  • Class renames: the tables, diff blocks and "survive as" sentence of the library's MIGRATION.md (38 renames from 0.23.0, each checked against the stylesheet). The guide starts at 0.23.0, so classes of older releases (0.7.x, for example) are not recognised; names the guide gives no pui-* class for (dark, dropdown-trigger, field-group-error) and the removed utilities are not reported.

  • Install instructions: read from the installation document and pinned to the package version; every CDN file and import is checked against the package's files and exports before it is written.

The library is read from PERFECTUI_SOURCE (a local checkout, as it is on disk) when set, else from .cache/library/v1.0.0/, else from one download of the tag's archive from GitHub. The package comes from .cache/package/1.0.0/, else from npm pack. Only this build step uses the network; the server does not.

Development

Task

Command

Tests (offline)

npm test

Type-check source, scripts and tests

npm run typecheck

Build dist/

npm run build

Rebuild the corpus

npm run corpus

Start on stdio

npm start

List what the package ships

npm pack --dry-run

Count invented classes in saved eval outputs (evals/cases.json)

npx tsx scripts/eval-markup.ts --runs evals/runs/<date>/<model>

The tests call every tool through the MCP SDK client over an in-memory transport, so they exercise the same schemas, validation and errors a real client sees. Design notes on the transport are in docs/spikes/stdio.md.

Releases

A release is a tag v<version> that matches package.json, pushed by the owner. .github/workflows/publish.yml then checks that the tag is on main, runs the same checks as CI, publishes to npm with trusted publishing (no npm token is stored; npm attaches provenance) and creates the GitHub release. A version with a prerelease suffix (1.0.0-beta.0) is published under the next dist-tag, never latest.

License

MIT, see LICENSE. data/corpus-1.0.0.json contains the Perfect UI documentation, also MIT, copyright Christopher Gonçalves. The summary parser in scripts/build-corpus.ts is adapted from perfectui-doc (MIT), so slugs match the documentation site.

Available Tools

5 tools
check_markupCheck markupA
Read-onlyIdempotent

Lists every pui-* class in the HTML that Perfect UI 1.0.0 does not define (kind "unknown"), with its line and the closest real class, and every Perfect UI 0.23.0 class that the migration guide renames (kind "legacy"), with the guide's replacement. Reads class and className attributes only; other classes (your own, Tailwind) are ignored. Call it on any markup before returning it.

ParametersJSON Schema
NameRequiredDescriptionDefault
htmlYesHTML, JSX or a template, up to 100000 characters

Output Schema

ParametersJSON Schema
NameRequiredDescription
validYes
checkedYes
versionYes
findingsYes

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, openWorldHint=false and destructiveHint=false, so safety is covered. The description adds real behavioral scope beyond that: only class and className attributes are inspected, and the user's own or Tailwind classes are ignored — a meaningful constraint on the analysis.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight sentences, front-loaded with the two result kinds before the scoping caveat and the call-to-action. Dense but every sentence carries information; no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists and annotations cover safety, so return-value and side-effect details need not be repeated. The description still adds the key semantic distinction between 'unknown' and 'legacy' kinds plus the class-attribute scope, leaving nothing essential missing for a one-parameter read tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the single `html` parameter is fully documented with its type and length bounds. The description adds no syntax or format detail beyond what the schema provides, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource (lists pui-* classes in HTML) and precisely scopes the two output kinds the tool produces ('unknown' and 'legacy'). An agent can distinguish it from siblings like list_components or get_component without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says 'Call it on any markup before returning it,' giving a clear trigger condition. It does not name any alternative or when-not-to-use case, but the sibling tools are unrelated lookup tools, so the missing exclusion is minor.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_componentGet a documentA
Read-onlyIdempotent

The full Markdown of one Perfect UI 1.0.0 document, with its HTML examples and the pui-* classes they use. Copy markup from the examples instead of writing classes from memory. Names come from list_components.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesdocument slug, for example button, modal or dark-mode

Output Schema

ParametersJSON Schema
NameRequiredDescription
fileYespath in the library repository
slugYes
titleYes
classesYespui-* classes used in the examples
sectionYes
versionYes
examplesYesthe document's html code blocks
markdownYes
descriptionYes

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare the safe read-only, idempotent, closed-world profile, so the bar is lower. The description adds substantive content-level context: the return is full Markdown with HTML examples and the specific class names they use, telling the agent what it can and should do with the output.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two compact sentences, front-loaded with what the tool returns, followed by the practical instruction and the name source. No filler or redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, the description needn't explain return values, and annotations carry the safety profile. It covers purpose, return nature, parameter source, and usage intent. Only the relationship to non-list siblings (search_docs, get_install, check_markup) is left unaddressed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single name parameter is fully documented in the schema (100% coverage plus a 28-value enum). The description adds value beyond the schema by telling the agent where valid names come from (list_components), which is not restated in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource: returns the full Markdown of one Perfect UI document, including HTML examples and the pui-* classes used. That is concrete enough for an agent to know exactly what it gets back. It only partially differentiates from siblings, naming list_components as the source of names but not contrasting with get_install or search_docs.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an actionable usage rule ("Copy markup from the examples instead of writing classes from memory") and a routing hint for the name argument ("Names come from list_components"). No explicit when-not-to-use or contrast with search_docs/check_markup, so it falls short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_installGet install instructionsA
Read-onlyIdempotent

How to install Perfect UI 1.0.0: the npm command and the other package managers' commands, the CDN stylesheet and script URLs and the import statements, all pinned to this version, from the library's installation document (get_component with name installation explains when the script is needed).

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
cdnYes
npmYesnpm command that installs the pinned version
sourceYeslibrary file the commands come from
importsYesimport statements from the installation document
packageYes
versionYes
packageManagersYesinstall command per package manager, pinned

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, destructiveHint false, and openWorldHint false, so safety behavior is covered. The description adds useful context beyond annotations: version pinning, source document, content types (npm commands, CDN URLs, import statements), and a cross-reference to get_component. No auth or rate-limit details, but they are not essential for this simple read tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single dense sentence that is front-loaded with the purpose ("How to install Perfect UI 1.0.0:") and then lists content. It is information-rich without being redundant, though the list is somewhat run-on.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Output schema exists, so return values need not be explained. Annotations cover safety profile. The description supplies content scope, version pinning, source document, and a related-tool pointer. For a zero-parameter retrieval tool, this is nearly complete, with only explicit usage routing missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Zero parameters, so baseline is 4. There is no parameter semantics to add or clarify.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States specific content and version (Perfect UI 1.0.0 install instructions: npm, other package managers, CDN URLs, import statements, all pinned). It also clarifies its relationship to get_component by noting that get_component explains when the script is needed. However, it does not distinguish itself from list_components or search_docs.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No explicit when-to-use or when-not-to-use guidance. The only routing hint is that get_component with name installation explains when the script is needed. Usage is implied by the description's content scope rather than stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_componentsList documentsA
Read-onlyIdempotent

Every document of Perfect UI 1.0.0, in the documentation's order: components, forms, layout, customization and guides, each with its slug (the name get_component takes), title, section and one-line description.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
versionYes
componentsYes

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint/idempotentHint/destructiveHint=false, so the safety profile is covered. The description adds the notable behavioral detail that results follow the documentation's own order and grouping (components, forms, layout, customization, guides), which is beyond structured fields, but says nothing about return size or pagination.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single front-loaded sentence with no filler; the section grouping and field list are packed into one clause and every element earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present and zero parameters, the description needn't explain return values, and it correctly focuses on scope and ordering. Only the absence of guidance on when to choose this over the sibling search/get tools leaves a small gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the baseline is 4; there is nothing for the description to clarify beyond what the empty schema already conveys.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (lists) and resource (every document of Perfect UI 1.0.0) and names the concrete fields returned (slug, title, section, description). The parenthetical 'the name get_component takes' explicitly ties the slug to the sibling tool's input, letting an agent distinguish it from get_component/search_docs.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is only implied: as a full catalog it is the natural first step before get_component, but the description never states when to prefer it over search_docs or when it is unnecessary. No explicit alternatives or exclusions are given.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_docsSearch the documentationA
Read-onlyIdempotent

Full-text search over the sections of every Perfect UI 1.0.0 document (prefix and typo tolerant). Returns the document slug (for get_component), the section heading (empty for the opening section), a snippet and a score, best first.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNohow many sections to return, 1 to 10 (default 5)
queryYeswords to look for, for example 'modal backdrop' or 'dark mode'

Output Schema

ParametersJSON Schema
NameRequiredDescription
queryYes
resultsYes

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover read-only, idempotent, non-destructive, closed-world safety. The description adds meaningful behavior beyond that: prefix and typo tolerance, best-first ranking, and the shape of returned fields including the empty heading for the opening section.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single dense sentence front-loads the core action, then the matching semantics, then the return shape and ordering. No filler, everything earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter, enums-free search tool with a full input schema, full schema coverage, and an output schema, the description supplies everything needed: matching behavior, ranking, and how results chain into get_component.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, with limit's range and default and query's format fully documented, so the schema does the heavy lifting. The description adds no extra parameter syntax or constraints beyond it, which is the baseline-3 case.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (full-text search) over a specific resource (sections of every Perfect UI 1.0.0 document), plus the matching behavior (prefix and typo tolerant). It also separates itself from sibling get_component by noting the returned slug is what feeds that tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies the workflow clearly: search here first, then use the returned slug with get_component. There is no explicit when-not guidance or named alternative for other discovery paths (e.g., list_components), but the context is unambiguous.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 5 tool updatesv0.1.0
    • First observedcheck_markup
    • First observedget_component
    • First observedget_install
    • First observedlist_components
    • First observedsearch_docs

TDQS

A4.2/5.0

Scored across 5 tools

Disambiguation4/5

get_install and get_component both retrieve documentation content for installation, but get_install is a specialized shortcut for install commands while get_component returns full Markdown of any document; descriptions clarify the boundary. Other tools (list_components, search_docs, check_markup) have clearly distinct purposes. Minor overlap prevents a perfect score.

Naming Consistency5/5

All tool names follow a consistent snake_case verb_noun pattern (get_install, list_components, search_docs, get_component, check_markup). This makes the set predictable and easy to parse.

Tool Count5/5

Five tools cover the essential operations for a UI library documentation server: installation lookup, listing documents, searching, retrieving a document, and validating markup. Each tool has a clear role and the set is well-scoped.

Completeness5/5

The surface covers installation, discovery (list, search), retrieval, and validation, including legacy class migration. No obvious dead ends for the stated purpose of helping agents use Perfect UI correctly; the documentation lifecycle is fully covered.

Maintenance

ActivityNo data
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers