Prebuilt, parameterized, interactive HTML widgets for MCP Apps — in Go, out of the box.
gomukit lets an MCP server ship CRUD-style UI — data tables, card carousels,
forms — as fully self-contained HTML template resources: inline CSS, inline JavaScript,
zero external files, everything embedded in your single Go binary. Widgets
speak the official MCP Apps extension (io.modelcontextprotocol/ui, spec
2026-01-26) and render in any compliant host (Claude, ChatGPT, VS Code,
Cursor, Goose, Postman, …).
Status: pre-release. APIs are not stable yet.
https://gomukit.techthos.systems/mcp
Add that URL as a custom connector in Claude or ChatGPT and ask for a table, a form, a card list, a date picker or the menu — or ask to delete a customer and get the confirmation; every widget renders interactively inside the chat. No checkout, no install.
- Claude (web, desktop): Settings → Connectors → Add custom connector → paste the URL. Available on plans that allow custom connectors.
- ChatGPT: Settings → Connectors (developer mode) → add an MCP server with the same URL.
It is the examples/preview server described below, running with -sandbox:
every MCP session gets its own copy of the demo data, so you can use every
writing tool and no one else sees your edits.
Table and Form widgets rendered by the examples/harness fake host — light and host dark themes.
CardList lays a collection out as a horizontally scrolling card carousel that fits a narrow chat pane (same filter/sort/pagination/selection as Table); Card renders a single record.
Confirm asks before something irreversible happens: details, effects, and optional guards (acknowledge, type-to-confirm) — the accept button stays disabled until they are satisfied.
package main
import (
"context"
"net/http"
"github.com/modelcontextprotocol/go-sdk/mcp"
"github.com/techthos/gomukit"
"github.com/techthos/gomukit/gosdk"
)
func main() {
table := &gomukit.Table{
URI: "ui://myapp/users",
Title: "Users",
Columns: []gomukit.Column{
gomukit.Text("name", "Name"),
gomukit.Number("balance", "Balance", "currency:EUR"),
gomukit.Badge("status", "Status", map[string]gomukit.BadgeVariant{
"active": gomukit.BadgeSuccess,
}),
},
Filterable: true,
PageSize: 10,
}
server := mcp.NewServer(&mcp.Implementation{Name: "myapp"}, gosdk.EnableUI(nil))
type in struct{}
type out struct {
Rows []map[string]any `json:"rows"`
}
gosdk.AddWidgetToolFor(server, table,
&mcp.Tool{Name: "list_users", Description: "List users in a table."},
func(context.Context, *mcp.CallToolRequest, in) (*mcp.CallToolResult, out, error) {
rows, _ := gomukit.RowsOf(loadUsers())
return nil, out{Rows: rows}, nil
})
h := mcp.NewStreamableHTTPHandler(func(*http.Request) *mcp.Server { return server }, nil)
http.ListenAndServe(":8080", h)
}Ask a connected assistant to "list the users" and it renders an interactive, host-themed table — sortable, filterable, paginated — inside the chat.
- Table: typed columns (text/number/date/badge/link/actions), client-side sort/filter/pagination, row selection with bulk actions, per-row actions → MCP tool calls, inline destructive-action confirmation, empty/loading states.
- Form: 11 field types, native + inline client validation, submit as a tool call, server-side field errors mapped inline, prefill for edit flows.
- DatePicker: a date or a range, as a widget of its own or as a form field. One calendar either way: bounded windows, blocked days, quick-range presets, ISO week numbers, month/year travel, full keyboard control.
- Menu: a launcher grid of tiles, one per UI-backed tool — the app's front door. Choosing a tile calls that tool so the host opens its widget.
- Confirm: a full-widget confirmation before an irreversible tool call — record details, the effects of the action, and optional guards (acknowledge checkbox, type-to-confirm) that gate the accept button.
- Host-aware theming:
--gomu-*design tokens defaulting to host-injected CSS variables (Claude/ChatGPT look automatic),Themestruct overrides, dark mode. - Locale-aware: numbers/dates formatted via
Intlwith the host's locale and time zone. - SDK-agnostic core + adapter for the official go-sdk; the core works with any Go MCP implementation.
- Self-contained by construction: documents satisfy the spec's default locked-down CSP; no CDN, no network, no files on disk.
- Widget reference — Table, Form, Card, CardList, Menu, Confirm, Choice, DatePicker, actions, data contract
- Theming — tokens, host variables, dark mode
- Architecture — rendering model, runtime, security
- Preview server — every widget as real MCP tools, for inspectors
examples/demo— complete MCP server (streamable HTTP or-stdio): list/edit/save/delete/archive users, and book a follow-up call on a date picker whose free days are computed per call. Point MCPJam or any MCP Apps host athttp://localhost:8080/mcp.examples/preview— the widest MCP server: a small app with mutable state (customers, orders, forms, confirmations, choices) plus a gallery of every widget variant as its own tool. Built for driving from an MCP Apps capable inspector.make preview, then point the inspector athttp://localhost:8081/mcp, or use the hosted instance athttps://gomukit.techthos.systems/mcpwithout running anything. See docs/preview.md.examples/harness— a fake MCP Apps host in one HTML page, with a story browser: pick a widget variant from the rail (table, cardlist, card, form, menu, plus empty and long-list states), see it rendered in a sandboxed iframe at any viewport width, and watch the JSON-RPC traffic. It answers the handshake, replies to tool calls, and simulates tool results/errors and theme changes.go run ./examples/harness, openhttp://localhost:8090.
Both of the last two run from one container image, so the widgets can be put on a URL for other people to try in their own chat client:
curl -O https://raw.githubusercontent.com/Techthos/gomukit/main/examples/docker-compose.yml
docker compose up -d # harness on :8090, preview MCP on :8081/mcpThe preview service runs with -sandbox, which gives every MCP session its
own copy of the scenario data: visitors can use every writing tool, and no
one's edits reach anyone else. See
docs/preview.md.
The TypeScript/CSS runtime lives in ui/ and is bundled with esbuild into
internal/assets/dist/ (committed, go:embed-ed — consumers never need
Node).
make assets # npm ci + build the runtime bundle
make test # go test ./... + vitest
make verify-dist # fail if committed dist doesn't match ui/ sources
make build # build the example servers into ./binGolden-file tests: go test ./ -update regenerates testdata/golden/.
MIT





