Connection lost. Reconnecting… attempt 1 of 8
Paused. Your work is held on the server.
Could not reconnect.
This session has expired on the server.
DR.Simple_UI
Showing main. This can be ahead of the version your app has installed — check what a class says it needs before copying it. Releases (opens in a new tab)

MCP server

this site, not the package

This site answers to agents as well as to people. One read-only endpoint, six tools and four resources, over the same bytes the pages render.

Public, unauthenticated and read-only. Every tool is annotated readOnlyHint, so a client that honours the hint calls them without prompting. There will never be a seventh tool that writes — that is what makes calling these unprompted safe, and it is enforced by a test rather than by intention.

Add it

One URL. Streamable HTTP, no key, no install.

Any MCP client

The endpoint is https://simpleui.dennisrahmen.dev/mcp. It is stateless, so a client may reconnect or fan out across processes without leaving sessions behind on the server.

{
  "mcpServers": {
    "dr-simple-ui": {
      "type": "http",
      "url": "https://simpleui.dennisrahmen.dev/mcp"
    }
  }
}

Claude Code

Or add it to a project's .mcp.json with the block above.

claude mcp add --transport http dr-simple-ui https://simpleui.dennisrahmen.dev/mcp

The six tools

Six verbs, no overlap. They are shaped around one workflow — an agent writing a page in a consuming app, knowing roughly what it wants and needing the exact markup plus enough semantics to pick the right variant.

ToolWhat it answers
search “What is there for a sortable table with status badges?” Ranks examples, classes, tokens and pages together. Returns references, never markup — a search that returned markup would spend the context window on the first call.
get_example The markup for up to five examples, byte-for-byte what this site renders. Valid in a .razor page and an .html file alike.
describe_class What a class actually does: the declarations from the shipped stylesheet, its cascade layer, its modifiers, and which examples use it. Also the way to check a class your app already styles before upgrading.
get_page Every example on one page — one call instead of five. With no id, the page list.
get_tokens The tokens as an ordered array of blocks, media condition included, for writing brand.css.
get_integration_guide The host page and its load order, the branding recipe, the JavaScript and C# surface, or the rules a consuming app follows.

The whole loop

searchget_exampledescribe_class. Nothing else is needed to write a page.

search { "query": "filter bar above a sortable table", "installedVersion": "0.2.0" }

  → hits: [ { kind: "page",    ref: "/toolbar", title: "Toolbar",    score: 78 },
            { kind: "example", ref: "Toolbar/FilterBar",   classes: ["toolbar", "toolbar-field", …] },
            { kind: "example", ref: "Table/SortableTable", classes: ["table", "table-sortable", …] },
            { kind: "class",   ref: ".table-sticky", blurb: "In layer dr.paint. Used by 2 example(s)." } ]

get_example { "ids": ["Toolbar/FilterBar", "Table/SortableTable"] }

  → the markup, byte-for-byte what this site renders, plus every class it uses
    and the release each one first shipped in.

describe_class { "names": ["table-sortable", "col-num"] }

  → the declarations the shipped stylesheet holds for each, its cascade layer,
    its modifiers, and which examples use it.

The four resources

Whole artefacts, addressable by URI. A resource is attached by the person, not pulled by the model — which is the right party to decide whether 200 KB of stylesheet belongs in a context window.

URITypeWhat it is
drsimpleui://stylesheet text/css The whole shipped stylesheet, exactly as the package delivers it.
drsimpleui://tokens application/json The token export, as an ordered array of blocks.
drsimpleui://version application/json What this catalogue was built from, and the latest released version.
drsimpleui://docs/{name} text/markdown One documentation file: getting-started, architecture, CLAUDE.consuming-app, releasing.
Nothing is resource-only. Clients vary widely in resource support and several ignore them entirely, so get_tokens and get_integration_guide duplicate two of these on purpose: the tool is the guaranteed path, the resource the ergonomic one.

Say which version you have

This site is built from main and can be ahead of any release. Every tool takes installedVersion.

Pass it and the meta block names anything your version does not have — “3 of these are not in 0.2.0: .popover, .search-panel, --brand-glow. Upgrade DR.Simple_UI, or use something else.” The results still come back: filtering them out silently would hide that an upgrade is the fix, which is usually the right answer. Every class and token also carries its own since.

Limits

LimitValueWhy
Concurrent requests16, plus 8 queued The actual control. Not partitioned, so there is nothing to spoof around.
Per caller60 burst, 30/min Fairness, not security: behind a proxy whose addresses we do not control, per-IP limiting is not a security measure. A token bucket because real agent traffic is bursty — six get_example calls, then nothing.
Request body256 KB JSON-RPC calls here are a few hundred bytes.
Per call 25 / 5 / 10 Hits from search, ids per get_example, names per describe_class. Markup is capped at 8 KB per example and cut at a tag boundary, so a truncated response still parses.
The limits apply to /mcp alone, never globally. A global limiter would also count this site's own SignalR upgrades and every static asset, so one person browsing would trip a ceiling sized for tool calls. A rejected call answers 429 with Retry-After: 60.

Then give the agent the rules

The server answers questions. It does not stop an agent inventing a class name or wrapping a table in a component.

Copy docs/CLAUDE.consuming-app.md (opens in a new tab) into your app's own CLAUDE.md, or fetch it through get_integration_guide { "section": "rules" }. It is the short list of things that go wrong: inventing token names, restyling the frame, and reaching for a component where a class exists.

describe_class

Up to ten class names per call. The declarations come from the stylesheet the running app serves, so they are what ships rather than a transcription of it, and usedByExamples points at the pages that show the class in context.

Pass installedVersion and meta.warning names everything in the answer that your version does not have. That is the whole reason since exists: this site is built from main and can be ahead of the package you installed.

describe_class { "names": ["health-badge", "stat-target"], "installedVersion": "0.4.0" }

{
  "meta": {
    "branch": "main",
    "commit": "9ac438c",
    "builtUtc": "2026-08-03T11:20:44Z",
    "latestRelease": "0.4.0",
    "installedVersion": "0.4.0",
    "warning": "2 of these are not in 0.4.0: .health-badge, .stat-target. Upgrade DR.Simple_UI, or use something else."
  },
  "classes": [
    {
      "name": ".health-badge",
      "layer": "dr.paint",
      "declarations": ["display: inline-flex", "align-items: center", "gap: var(--space-3)", "…"],
      "modifiers": [".health-badge--live", ".health-badge--streaming", ".health-badge--down"],
      "usedByExamples": ["/status#a-connection-s-state"],
      "since": "unreleased"
    },
    {
      "name": ".stat-target",
      "layer": "dr.paint",
      "declarations": ["font-size: var(--text-2)", "color: var(--muted)"],
      "modifiers": [],
      "usedByExamples": ["/stat#target-link-and-unavailable"],
      "since": "unreleased"
    }
  ],
  "notFound": []
}

# `declarations` is read from the stylesheet the running app serves, so it is
# what ships rather than a transcription. `since` comes from the git tags via
# build/class-history.sh, and `meta.warning` names everything in the answer
# that the version you passed does not have — so an agent can tell "copy this"
# from "upgrade first" without asking. At most ten names per call.