MCP server
this site, not the packageThis 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.
readOnlyHint, so a client that honours the hint calls them without prompting, and
there will never be a seventh tool that writes — enforced by a test.
Add it
One URL. Streamable HTTP, no key, no install.
Any MCP client
The endpoint is https://www.sedna-ui.com/mcp. It is stateless, so a client may reconnect
or fan out across processes without leaving sessions behind.
{
"mcpServers": {
"sedna-ui": {
"type": "http",
"url": "https://www.sedna-ui.com/mcp"
}
}
}
Claude Code
Or add it to a project's .mcp.json with the block above.
claude mcp add --transport http sedna-ui https://www.sedna-ui.com/mcp
The six tools
Six verbs, no overlap, shaped around one workflow: an agent writing a page in a consuming app and needing the exact markup plus enough semantics to pick the right variant.
| Tool | What it answers |
|---|---|
search |
“What is there for a sortable table with status badges?” Ranks examples, classes, tokens and pages together. Returns references, never markup. |
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
search → get_example → describe_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 sedna.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.
describe_class
Up to ten class names per call. The declarations come from the stylesheet the running app
serves, and usedByExamples points at the pages that show the class in context.
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 Sedna.UI, or use something else."
},
"classes": [
{
"name": ".health-badge",
"layer": "sedna.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": "sedna.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.
The four resources
Whole artefacts, addressable by URI. A resource is attached by the person rather than pulled by
the model. Nothing is resource-only: get_tokens and
get_integration_guide duplicate two of these, because clients vary widely in
resource support.
| URI | Type | What it is |
|---|---|---|
sednaui://stylesheet |
text/css |
The whole shipped stylesheet, exactly as the package delivers it. |
sednaui://tokens |
application/json |
The token export, as an ordered array of blocks. |
sednaui://version |
application/json |
What this catalogue was built from, and the latest released version. |
sednaui://docs/{name} |
text/markdown |
One documentation file: getting-started, architecture,
CLAUDE.consuming-app, releasing.
|
Say which version you have
This site is built from main and can be ahead of any release, so every tool takes
installedVersion. Pass it and the meta.warning block names everything
in the answer your version does not have — including anything with no since at all,
which is in no release yet. The results still come back.
Limits
They apply to /mcp alone, never globally — a global limiter would also count this
site's own SignalR upgrades. A rejected call answers 429 with
Retry-After: 60.
| Limit | Value | Why |
|---|---|---|
| Concurrent requests | 16, plus 8 queued | The actual control. Not partitioned, so there is nothing to spoof around. |
| Per caller | 60 burst, 30/min | Fairness, not security: behind a proxy whose addresses we do not control, per-IP limiting is not a security measure. |
| Request body | 256 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.
|
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 — so 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" }.