cd /news/developer-tools/prebuilt-parameterized-interactive-h… · home topics developer-tools article
[ARTICLE · art-71769] src=github.com ↗ pub= topic=developer-tools verified=true sentiment=· neutral

Prebuilt, parameterized, interactive HTML widgets for MCP Apps

Techthos released Gadget, a Go library that lets MCP servers ship prebuilt, parameterized, interactive HTML widgets such as data tables, card grids, and forms as self-contained embedded resources. The widgets implement the official MCP Apps extension spec (io.modelcontextprotocol/ui, 2026-01-26) and render in compliant hosts including Claude, ChatGPT, VS Code, Cursor, Goose, and Postman. The library is pre-release with unstable APIs.

read2 min views2 publishedJul 24, 2026
Prebuilt, parameterized, interactive HTML widgets for MCP Apps
Image: source

Prebuilt, parameterized, interactive HTML widgets for MCP Apps — in Go, out of the box.

gadget

lets an MCP server ship CRUD-style UI — data tables, card grids, 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.

Table and Form widgets rendered by the examples/harness fake host — light and host dark themes.

CardList lays a collection out as a card grid (same filter/sort/pagination/selection as Table); Card renders a single record.

package main

import (
    "context"
    "net/http"

    "github.com/modelcontextprotocol/go-sdk/mcp"
    "github.com/techthos/gadget"
    "github.com/techthos/gadget/gosdk"
)

func main() {
    table := &gadget.Table{
        URI:   "ui://myapp/users",
        Title: "Users",
        Columns: []gadget.Column{
            gadget.Text("name", "Name"),
            gadget.Number("balance", "Balance", "currency:EUR"),
            gadget.Badge("status", "Status", map[string]gadget.BadgeVariant{
                "active": gadget.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, _ := gadget.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/ states.Form: 10 field types, native + inline client validation, submit as a tool call, server-side field errors mapped inline, prefill for edit flows.Host-aware theming:--gadget-*

design tokens defaulting to host-injected CSS variables (Claude/ChatGPT look automatic),Theme

struct overrides, dark mode.Locale-aware: numbers/dates formatted viaIntl

with the host's locale and time zone.SDK-agnostic core+ adapter for the officialgo-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, actions, data contractTheming— tokens, host variables, dark modeArchitecture— rendering model, runtime, security

examples/demo

— complete MCP server (streamable HTTP or-stdio

): list/edit/save/delete/archive users. Point MCPJam or any MCP Apps host athttp://localhost:8080/mcp

.examples/harness

— a fake MCP Apps host in one HTML page: renders widgets in a sandboxed iframe, answers the JSON-RPC handshake, logs all traffic, simulates tool results/errors and theme changes.go run ./examples/harness

, openhttp://localhost:8090

.

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

Golden-file tests: go test ./ -update

regenerates testdata/golden/

.

MIT

── more in #developer-tools 4 stories · sorted by recency
── more on @techthos 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/prebuilt-parameteriz…] indexed:0 read:2min 2026-07-24 ·