Preview. The Allternit Apps SDK packages and the allternit CLI are not published to npm yet, so the install commands on this page do not work outside the Allternit repository today. To build an MCP App now, follow the Quickstart, which uses the open MCP SDK directly. Your app will not need changes to adopt the SDK later.
@allternit/apps-server is a thin layer over the official MCP SDK v2 (@modelcontextprotocol/server 2.3.1) and @modelcontextprotocol/ext-apps 2.0.3. It does not hide them: app.createServer() returns a normal McpServer. Every app it builds is dual-era by default: it speaks MCP 2026-07-28 (stateless, server/discover, a _meta envelope on each request) and still answers clients that open with the older initialize handshake. Tools are listed in name order and Views in URI order, so tools/list and resources/list are the same on every request. Tool input shapes are Zod 4 (the z re-exported by the package).

defineApp

defineApp throws on mistakes you would otherwise find in review: duplicate tool names or View URIs, a tool that points at a View you did not list, a malformed or over-broad CSP domain, an invalid look pack. It returns an app with four members:

appTool

readOnlyHint and destructiveHint are required and must be booleans. Stating them is the point: the host decides when to ask the user, and directory review checks them against the tool’s name. A tool cannot be both read-only and destructive. When a tool has a view, the SDK registers it with registerAppTool, which sets _meta.ui.resourceUri and the older flat _meta["ui/resourceUri"] key. z is re-exported from zod so the input shape and your code use one version.

view

A View is a ui:// resource. Build it from the view kit or write the HTML yourself.
The app’s look pack is written into every View as CSS variables, so a Views’ colors come from the pack and change when it does. A kit View also runs a small script that speaks the standard bridge: ui/initialize, host theme variables, ui/notifications/tool-result and size changes.

Serve it

listen runs streamable HTTP in stateless mode, a fresh server per request, for both protocol eras. It also answers GET /healthz and GET /allternit.app.json. allternit dev calls it for you. Pass onerror to see tool errors (the default logs them to stderr). To deploy somewhere that takes a web-standard fetch handler (Cloudflare Workers, Bun, Deno), use the handler directly:

Events (optional)

An app can offer MCP Events so a host subscribes once and your server calls it back when something happens. Add events to defineApp; see the Events page for the full example.

Validating a manifest

validateManifest applies the plugin package rules (name length, required annotations, Views that exist) plus the look pack validator. The scan rules are the same code directory review uses: scanInput, lintTools, lintResources.