MCP server
this site, not the packageThis site answers to agents as well as to people. One read-only endpoint, its 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 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 tools
One verb each, 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 — then checking what it wrote, and upgrading the app later.
| 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; a class a release renamed comes back with the classes that replace it. |
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, the rules a consuming app follows, the graph's or the rich-text
editor's reference, or — with "upgrade" — every class
each release renamed, old name to the classes that look the same.
|
check_markup |
For markup the agent wrote: every class the stylesheet does not declare, with the nearest library class in case it is a typo; every class a release renamed or removed, judged against the installed version; every class newer than it; and every icon the font does not have. Classes only — it does not judge structure. |
get_changes |
What two releases differ by: every class, token and public C# member added and
removed, every rename, and each release in between with a link to its notes,
where the breaking changes are listed first. to: "main" includes
what is merged and in no release yet.
|
describe_api |
The C# and script API, member by member: a type's members, a member's signatures,
its documentation, the release it first shipped in and the examples that use it
— ISednaUi.ShowModalAsync, or sednaUi.toast.
|
The whole loop
search → get_example → describe_class. Nothing else
is needed to write a page; check_markup then checks it.
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 |
Any file in the repository's docs/, by name without
.md — getting-started, architecture,
CLAUDE.consuming-app and the rest. A wrong name is answered with the list.
|
Say which version you have
This site is built from main and can be ahead of any release, so every tool that
answers with classes, tokens, examples or API 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 | 64, plus 32 queued | The actual control. Not partitioned, so there is nothing to spoof around. |
| Per caller | 240 burst, 120/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. describe_api
takes 10 names, and check_markup up to 64 KB of markup.
|
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" }.