# Skybridge changelog

Open-source TypeScript framework for building MCP Apps that run in Claude, ChatGPT, VSCode, and any MCP client.

Source: https://github.com/alpic-ai/skybridge/releases

## v2.0: North Face (Oct 15, 2026)

No lift runs to the north face. You climb it, and you pick the line yourself because nobody has left tracks in it.

MCP shipped the `2026-07-28` revision this summer and no framework was on it yet. Getting there meant rebuilding the part of Skybridge nobody sees: how a request becomes a server. One `/mcp` endpoint now serves both the 2025-era hosts you already support and the new revision, and every request gets its own server instance instead of sharing one long-lived singleton.

The other unmarked line is testing. A tool that works is not the same as a tool the model decides to call, and until now there was no way to check the difference. `@skybridge/test` runs a real conversation against your app and asserts on the calls the model actually made.

Alongside those: schema validation that is no longer zod-only, and a hooks surface that groups things where you expect to find them.

**This is a major, so there are breaking changes.**

The full list, with before and after, is in the changelog below, and your coding agent can do most of it for you.

---

### MCP 2026-07-28, and why the server got rebuilt

The new protocol revision changes how results are serialized, and in SDK v2 the negotiated era is state on the server instance. Skybridge used to keep one `McpServer` for the process lifetime, which meant that instance never negotiated anything: it would have served 2026-era clients with the 2025 codec, and one client's `initialize` would have overwritten another's.

So registration moved into a handler the framework calls for each request. Your tools, resources, prompts and views are registered on the instance that serves the request, which is the one the SDK actually stamps with the negotiated version.

You see this as the new `Skybridge` app. The implementation info, the SDK options and Skybridge's own options merge into one config object, and the tool chain moves into its `handler` field:

```ts
// before
const server = new McpServer(
  { name: "my-app", version: "1.0.0" },
  { capabilities: {} },
  { oauth },
).registerTool({ name: "search", ... }, handler);

export default await server.run();
export type AppType = typeof server;
```

```ts
// src/server.ts, after
export const app = new Skybridge({
  name: "my-app",
  version: "1.0.0",
  oauth: descopeProvider({ url: env.DESCOPE_URL }),
  handler: (server) => server.registerTool({ name: "search", ... }, searchHandler),
});

export type AppType = typeof app;
```

```ts
// src/index.ts, after
import { app } from "./server.js";

export default await app.run();
```

`server.ts` stays the complete definition of your app, `index.ts` only runs it, and tests can import the app without triggering the run.

The handler must **return** the chained server, that is what carries your tool types into `typeof app`. Everything else is inferred from the config too: pass a provider to `oauth` and `extra.http.authInfo.extra` is typed with its claims in every tool handler, no annotation needed. Keep the handler inline for that to work; an extracted handler needs a hand-written `McpServer` type and loses the inference, which is why the `SkybridgeServer` alias from the v2 betas is gone.

There is no module-scope server object any more, so anything you used to do to one after startup, `registered.disable()` or a raw `setRequestHandler`, moves inside the handler. Outside it there is nothing to call them on, which the compiler will tell you.

`mcpMiddleware` follows the same rule: it is per-instance state now, so it chains inside the handler, and ordering within the chain is unchanged:

```ts
export const app = new Skybridge({
  ...config,
  handler: (server) =>
    server
      .mcpMiddleware(intentMiddleware())
      .registerTool(...),
});
```

#### Keep the handler pure

This is the one part of the change that can bite you in production, and neither the compiler nor a manual test will catch it. The handler body runs on every request, so registration is the only work that belongs inside it:

```ts
// WRONG: one pool per request
export const app = new Skybridge({
  ...config,
  handler: (server) => {
    const pool = new pg.Pool();
    return server.registerTool(...);
  },
});
```

```ts
// RIGHT: setup runs once, the handler receives its result
export const app = new Skybridge({
  ...config,
  setup: () => new pg.Pool(),
  handler: (server, pool) => server.registerTool(...),
});
```

The same goes for file reads, config parsing and client construction. This matters most when migrating, because in v1 those statements sat at module scope in the same file and the tempting move is to wrap the lot in the handler. If a handler takes longer than 50ms, Skybridge warns once in the console.

`setup` runs once, at `run()` or on the first request and never at module import, and its result is passed to the handler as the second argument. The handler stays synchronous:

```ts
export const app = new Skybridge({
  ...config,
  setup: async () => loadConfig(),
  oauth: (cfg) => descopeProvider({ url: cfg.mcpServerUrl }),
  handler: (server, cfg) => server.registerTool({ name: cfg.toolName, ... }, searchHandler),
});
```

`oauth` takes a provider, a raw config object, or a function of the `setup` result returning either. Providers are deferred: `descopeProvider(...)` validates its arguments but performs no network call until `run()` resolves it, so importing `server.ts` from tests and evals never reaches your IdP.

### Evals

A tool that works is not the same as a tool the model decides to call. `@skybridge/test` closes that gap: it runs a real conversation against your app, in process, and hands you the calls the model made.

`@skybridge/test` ships as a beta alongside this release: install it with `@skybridge/test@beta`, expect the matcher API to move in minors, and pin the exact version in CI if that matters to you.

```ts
import { anthropic } from "@ai-sdk/anthropic";
import { start } from "@skybridge/test";
import { expect, it } from "vitest";
import { app } from "../src/server.js";

it("reaches the capitals tool from a natural prompt", async () => {
  const chat = await start({ app, model: anthropic("claude-sonnet-4-5") });
  await chat.send("Tell me about the capital of France");

  expect.chat(chat).toHaveCalledToolWith("explore-capitals", { name: "Paris" });
});
```

`toHaveCalledToolWith` and `toHaveCalledToolOnce` are typed against your own registry, so the tool name autocompletes and the argument object is checked against that tool's input schema. `toNeverHaveCalledTool`, `toHaveFailedToolCall` and `toHaveSaid` cover the negative and conversational cases. Add `evals: {}` to the Vite plugin options to wire the matchers into vitest, then `vitest run evals`.

No HTTP server, no port, no fixtures. The client dials your app through an in-process fetch, and each conversation gets its own MCP handler that closes with the test.

Add `@skybridge/test@beta` (the package is published on the `beta` dist-tag only while the API settles, so a bare `@skybridge/test` will not install it), `vitest`, `ai` and the provider package you want (`@ai-sdk/anthropic`, `@ai-sdk/openai`, …) as dev dependencies, name your scenarios `evals/*.eval.ts`, and run them with `vitest run evals`. Turning on `evals` also raises the per-scenario timeout to two minutes, since each turn is a live model call, and loads your `.env` so the provider key is picked up. Tune the timeout with `evals: { timeout }`.

For an app behind OAuth, claim an identity for the session:

```ts
const chat = await start({
  app,
  model: anthropic("claude-sonnet-4-5"),
  authInfo: { token: "eval", clientId: "evals", scopes: ["read"] },
});
```

Only token verification is skipped. Per-tool schemes and scope checks run for real against those claims, and `extra` carries whatever your verifier would have produced. Omit `authInfo` to exercise the anonymous path, auth challenges included. The `auth-descope` example ships a scenario to start from.

### Standard Schema

`inputSchema` and `outputSchema` accept any [Standard Schema](https://standardschema.dev) validator now, not just zod. Nothing changes if you are happy with zod. If you would rather use valibot or arktype, you can, and the inferred handler argument types follow.

The schema has to advertise itself as JSON Schema too, since that is what `tools/list` sends to the host: the accepted type is the SDK's `StandardSchemaWithJSON`. Zod 4 and ArkType implement it out of the box; valibot does through `@valibot/to-json-schema`. A validator that cannot emit JSON Schema is a compile error, not a silent gap in your tool listing.

One consequence of users bringing their own schemas: zod is a peer dependency of `skybridge` now, not a bundled copy, and it has to be 4.2 or later, since 4.0 and 4.1 make the SDK fall back to a converter that drops `.describe()` descriptions from the schema the model sees.

### Hooks, regrouped

`useLayout` mixed two unrelated things: a user preference that rarely changes, and viewport geometry that changes on every resize. Views that only wanted `theme` re-rendered on every resize. They are split now: `theme` joins `useUser`, next to `locale` and `userAgent`, and `maxHeight` and `safeArea` move to the new `useViewport`.

---

### How to migrate?

1. Install [the latest version of the skill](https://docs.skybridge.tech/guides/migrate)
2. Run the prompt inside your favorite coding agent: `Migrate my app based on https://github.com/alpic-ai/skybridge/releases/tag/v2.0.0`

Then validate, because a green build is not enough:

1. Install dependencies and check for unmet peer warnings on `@skybridge/devtools` or `@skybridge/vite-plugin`.
2. `tsc --noEmit`. This catches the handler return, the `extra` reshape, and every removed export.
3. `skybridge build`, then `skybridge start`, not just `skybridge dev`: it is the only step that exercises the compiled `dist/index.js` entry.
4. `skybridge dev`, open the view in devtools and confirm it renders.

A missing `src/index.ts` passes `tsc` and `skybridge build`; `skybridge dev` and `skybridge start` both fail on it, unless a leftover `nodemon.json` still points `dev` at `src/server.ts`, in which case `dev` silently starts nothing. Delete that file, the CLI default already runs `src/index.ts`. An impure handler passes everything, devtools included, and only fails under sustained traffic.

Having issues? Join [our Discord](https://discord.gg/2jc92tZQdn) to get help from the maintainers.

---

### Breaking changes

Ordered by how likely a v1 app is to hit them. Each is enforced by the compiler unless noted.

#### The Skybridge app replaces the McpServer singleton (PR #1008, #1070)

```ts
// before
const server = new McpServer(info, options, skybridgeOptions).registerTool(...);
```

```ts
// after
export const app = new Skybridge({ ...info, ...options, ...skybridgeOptions, handler: (server) => server.registerTool(...) });
```

One config object. The tool chain moves into `handler`, which must return it; one-time work goes in `setup`, whose result is the handler's second argument; `oauth` takes a provider, a config, or a function of the `setup` result. `mcpMiddleware` chains inside the handler too.

#### The server entry moves to src/index.ts (PR #1070)

`src/server.ts` exports the app, a new `src/index.ts` runs it, and `skybridge start` looks for `dist/index.js`. Not caught by the compiler: a missing `index.ts` only fails at `skybridge start`.

```ts
// before, src/server.ts
export default await server.run();
```

```ts
// after, src/index.ts
import { app } from "./server.js";

export default await app.run();
```

#### skybridge/vite moves to/vite-plugin (PR #1008)

Install `@skybridge/vite-plugin` as a devDependency. The eval matchers live at `@skybridge/vite-plugin/evals`.

```ts
// before
import { skybridge } from "skybridge/vite";
```

```ts
// after
import { skybridge } from "@skybridge/vite-plugin";
```

#### Dependencies: drop the SDK, add zod, bump devtools (PR #1008)

Delete `@modelcontextprotocol/sdk` from your dependencies and import from `skybridge/server`, which re-exports `ProtocolError`, `ProtocolErrorCode`, `OAuthError` and `OAuthErrorCode`. Add `zod` at `^4.2.0`, now a peer dependency (4.0 and 4.1 drop `.describe()` descriptions from the schema the model sees). Move `@skybridge/devtools` to v2 as well: `skybridge` v2 peer-depends on it, so a `^1.x` pin reports an unmet peer.

#### Tool handler extra is now the SDK's ServerContext (PR #1008)

```ts
// before
async (args, extra) => {
  const token = extra.authInfo?.token;
  const meta = extra._meta;
  extra.signal.throwIfAborted();
}
```

```ts
// after
async (args, extra) => {
  const token = extra.http?.authInfo?.token;
  const meta = extra.mcpReq._meta;
  extra.mcpReq.signal.throwIfAborted();
}
```

`signal`, `id`, `notify` and `send` all move under `extra.mcpReq`. The typed ChatGPT client hints stay on `extra.mcpReq._meta`.

#### The registerTool(name, config, handler) form is removed (PR #1008)

Not caught by the compiler in v1 either: this form was accepted at runtime only, never typed. If your app has it, it already failed `tsc`; v2 also drops the runtime tolerance.

```ts
// before
server.registerTool("search", { inputSchema }, handler);
```

```ts
// after
server.registerTool({ name: "search", inputSchema }, handler);
```

#### useLayout is removed (PR #1008)

`LayoutState` becomes `ViewportState`.

```tsx
// before
const { theme, maxHeight, safeArea } = useLayout();
```

```tsx
// after
const { theme } = useUser();
const { maxHeight, safeArea } = useViewport();
```

#### useHostInfo is renamed useHost (PR #1008)

Same return shape.

#### useDownload returns the function bare (PR #1008)

```tsx
// before
const { download } = useDownload();
```

```tsx
// after
const download = useDownload();
```

#### useToolInfo loses its idle state (PR #1008)

`status` starts at `"pending"` and never was `"idle"` at runtime. `ToolIdleState` and the `isIdle` field are gone from every state in the union.

```tsx
// before
const { isIdle, isPending } = useToolInfo("search");
```

```tsx
// after
const { isPending } = useToolInfo("search");
```

#### InvalidTokenError is removed (PR #1008)

```ts
// before
throw new InvalidTokenError("expired");
```

```ts
// after
throw new OAuthError("invalid_token", "expired");
```

#### Providers return a deferred OAuthProvider (PR #1008)

The branded providers and `customProvider` no longer return a promise. They return an `OAuthProvider`, an object whose `resolve()` runs discovery, and `oauth` accepts it directly. Drop the `await`; if you resolved a provider by hand to feed `requireBearerAuth`, call `resolve()`.

```ts
// before
oauth: await workosProvider({ domain, audience }),
const config = await customProvider({ issuer, audience });
```

```ts
// after
oauth: workosProvider({ domain, audience }),
const config = await customProvider({ issuer, audience }).resolve();
```

#### The oauth verify option is removed (PR #1008)

`verifier` is the only path, and it types the claims your handlers receive.

```ts
// before
oauth: { verify: { jwksUri, issuer, audience } }
```

```ts
// after
oauth: { oauthMetadata, verifier: createJwksVerifier({ jwksUri, issuer, audience }) }
```

#### KnownToolMeta.securitySchemes is removed (PR #1008)

Declare security schemes through the top-level tool config or the `auth` field instead of `_meta`. Not caught by the compiler: `ToolMeta` stays open (`Record<string, unknown>`), so `_meta: { securitySchemes }` still typechecks, is forwarded to the client untouched, and no longer enforces anything.

#### ViewConfig.hosts and window.skybridge.hostType are removed (PR #1008)

Every view emits a single ext-apps resource, so host targeting no longer applies. `ViewHostType` is gone, and the served view page no longer declares `hostType` on `window.skybridge`; the runtime is detected at load time via `window.openai`. A view reading `window.skybridge.hostType` should drop the check.

#### SDK v2 renames that reach tool handlers (PR #1008)

`McpError` is `ProtocolError` and `ErrorCode` is `ProtocolErrorCode`. `extra.http?.req` is a Web `Request`, so header reads use `.headers.get()`. `extra.mcpReq.send()` takes no result schema for spec methods. `extra.mcpReq.elicitInput()` exists but is deprecated by the SDK and throws on a 2026-07-28 request; on the new revision, return `inputRequired(...)` (re-exported from `skybridge/server`) from the handler instead, as the SDK's upgrade guide describes. `registerPrompt` and `registerResource` follow the SDK signatures: the variadic `.prompt()`/`.resource()` forms are gone, `argsSchema` is a schema object, and `registerResource` takes a metadata argument. The other OAuth error classes collapse into `OAuthError` + `OAuthErrorCode`. Not caught by the compiler: unknown-tool calls now reject with `ProtocolError` instead of resolving `isError: true`, and error messages lose the `MCP error <code>:` prefix. The SDK's [upgrade guide](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/migration/upgrade-to-v2.md) covers the rest.

#### Registration and OAuth errors surface at first use (PR #1008)

`setup`, `oauth` and the first run of `handler` happen at `run()` or on the first request, not when `new Skybridge(...)` executes. A duplicate tool name or an invalid `oauth.baseUrl` therefore rejects the first `run()` or `createServerInstance()` call instead of throwing from the constructor. Custom Express middleware registered with `app.use()` before `run()` now runs before the bearer check on `/mcp`; the route itself stays protected.

#### McpServer.connectStatelessTransport is removed (PR #1008)

Per-request instances make it meaningless: the framework connects a transport for every request. Apps that wired their own transport go through `app.run()` or `createServerInstance()` (below).

#### app.fetchHandler is removed and createServerInstance is async (PR #1008)

Only relevant if you ran a v2 alpha or beta; neither existed in v1. The HTTP surface an app exposes is the Express listener (`app.run()` / `app.express`). Code that dialed `app.fetchHandler` builds its own handler with `createMcpHandler(() => app.createServerInstance())` from `@modelcontextprotocol/server`. `createServerInstance()` returns a promise now, since it resolves `setup` and `oauth` on first use.

#### View tool inputSchema is zod-only (PR #1008)

Tools registered from a view via `useRegisterViewTool` take a zod shape (`ViewToolInputShape`); the bridge validates arguments with `z.object()` at runtime. Server-side schemas are unaffected and accept any Standard Schema validator.

#### RawInputShape and InferSchemaOutput are removed (PR #1008)

Short-lived aliases from earlier v2 alphas. `skybridge/server` re-exports the SDK's `StandardSchemaWithJSON`: use it for a schema and `Record<string, StandardSchemaWithJSON>` for a shape. For what a schema produces, use your library's own inference helper (`z.infer`, `v.InferOutput`, `typeof schema.infer`).

#### SkybridgeServerOptions is what McpServer reads (PR #1008)

Only relevant if you construct `McpServer` by hand: its third argument is now `{ oauth?: boolean; skills?: boolean }`. Everything else lives on the `Skybridge` config.

---


* Feat!: migrate to/sdk v2 (MCP 2026-07-28)
* Migrate the MCP client to sdk v2 and harden the browser OAuth flow
* Feat!: replace the registrar with a user-defined factory via the Skybridge app class
* Evals
* Add mcpcn example
* Add missing mcpcn showcase preview image
* Restore page heroes with new illustrations
* Scaffold a repo example via --example <name>
* Always pass (args, extra) to schema-less tool handlers
* QA feedbacks on the realistic UI preview
* Size the view iframe to the whole document so short widgets don't scroll
* Expose inspector in fullscreen
* Make npm publish idempotent per version

### v2.0.4 (Oct 15, 2026)

Skybridge devtools now validate tool input schemas that use JSON Schema draft 2020-12.

### v2.0.3 (Oct 6, 2026)

Skybridge devtools now support local OAuth servers using HTTP addresses under `*.localhost`, such as `chift.localhost:4000`. The bundled MCP client is updated to 2.2.0, fixing the sign-in error reported in #1119.

### v2.0.2 (Oct 5, 2026)

`@skybridge/test` is out of beta. It now ships on `latest` with the same version as the other Skybridge packages, so install it with `npm install -D/test`. Its peer range now accepts `ai` v7.

### v2.0.1 (Sep 29, 2026)

CLI telemetry now reports the AI coding agent that ran a command, if any.
## v1.4: Bluebird (Aug 24, 2026)

You dropped in, the run went well, and now the clouds have lifted. Bluebird day: the whole face is in front of you, and for the first time you can see your line before you ride it 🌤️

`v1.3.0` was about committing to the run. `v1.4.0` is about **seeing exactly where you're going**. DevTools now renders your app inside a mock ChatGPT or Claude conversation, on desktop and on mobile, so the thing you're building looks like the thing your users will get. And if commerce is your run, there's a full template waiting at the top of the lift.

- **Preview mode**: your widget inside a ChatGPT or Claude conversation shell, desktop and mobile, without leaving DevTools
- **Ecom template**: `npm create skybridge@latest --ecom` scaffolds a working shopping app, Shopify included
- **Skills over MCP, updated**: `skills/list` and `skills/get` per the current SEP-2640, so ChatGPT's skill importer works

Clear skies, fresh snow. Here's what's new 🏔️

---

### Preview mode

Until now, DevTools showed your widget in a panel. Useful, but a panel is not a conversation, and plenty of layout problems only appear once your view sits in a 768px column under a thread of messages.

Hit **preview** in the tool panel toolbar and the panel becomes a full ChatGPT conversation, with your real widget in it. Everything around the widget is skeleton (sidebar, thread, disabled composer), measured against the real client in light and dark. Switch the client and you get the same treatment for Claude. Toggle the device and the shell renders inside a 390 x 844 phone frame, with the mobile layout each client actually uses.

The host context is real too: `ui/initialize` carries the client's own style variables, container dimensions and available display modes while preview is active, so your view themes itself the way it will in production.

Two related changes come with it:

- **Display mode is now read-only** in the tool panel. It shows what the view asked for, and changes only when the view calls `requestDisplayMode`, which is how it works in a real client.
- **Fullscreen is bounded to the panel work area**, so the tools sidebar and header stay visible while you inspect.

The [DevTools guide](https://docs.skybridge.tech/test/devtools) covers the whole thing.

### Ecom template

`create-skybridge` gains a third template next to `demo` and `blank`. Pick **ecom** in the prompt, or skip it:

```bash
npm create skybridge@latest my-shop -- --ecom
```

You get a working shopping app: a `search-products` tool, a `render-carousel` tool, a product carousel with a detail view, variant picker, image gallery and a small design system, all with Ladle stories so you can work on the components on their own.

Data goes through a single seam. `src/catalog/index.ts` re-exports `search` and `getProducts`, and that is the only place the app talks to a backend. It ships pointing at a mock catalog, so the app runs before you have any credentials. Point it at `./shopify.js`, set `SHOPIFY_STORE_DOMAIN` and `SHOPIFY_STOREFRONT_TOKEN`, and you're on the Storefront API. Writing your own provider means exporting those same two functions.

The [ecommerce guide](https://docs.skybridge.tech/guides/ecommerce) walks through the whole thing.

### Skills over MCP, updated

`v1.3.0` shipped Skills over MCP against the pre-July draft of [SEP-2640](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640), which exposed skills as a `skill://index.json` resource. The SEP has since replaced that index with two mandatory methods, and declaring the extension now commits a server to both.

Skybridge implements `skills/list` and `skills/get`, which unblocks [ChatGPT's skill importer](https://developers.openai.com/plugins/build/mcp-server#import-skills-from-the-mcp-server): it enters through `skills/list` and used to get a `-32601` back. Nothing changes on your side, keep dropping `SKILL.md` under `src/skills/` with `skills: true` on the server.

> Still experimental while the SEP is under review.

### Also in this release

- **`authplaneProvider`**: [Authplane](https://authplane.com/) joins the branded OAuth providers, configuration only, no extra dependencies.
- **`useHostInfo`**: a view can now tell which host it renders in (`claude`, `chatgpt`, `cursor`, `goose`, …) and adapt copy, shortcuts or layout to it.
- **`getToolError`**: the MCP SDK swallows whatever a tool handler throws, so `mcpMiddleware` never saw the original `Error`. `getToolError(extra)` hands it back with stack and `cause` intact, which is what you want for Sentry.
- **Verifier messages** no longer leak into the `WWW-Authenticate` challenge unescaped.
- **Asset URL rewriting** at runtime now applies to JS only, leaving CSS alone.

---

* Update dependency postcss to ^8.5.20 by[bot] in https://github.com/alpic-ai/skybridge/pull/992
* Add claude to conformance test
* Add alpic playground
* Re-add examples/* to pnpm workspace
* Advertise the agentic AS's own discovery document
* Allow extending esbuild externals via skybridge({ serverExternal })
* Push images ids to ctx.imageIds
* Don't call adaptor.setViewState inside the React state updater
* Update actions/setup-node action to v7 by[bot] in https://github.com/alpic-ai/skybridge/pull/978
* Add useHostInfo hook to identify the rendering host
* Update all non-major dependencies by[bot] in https://github.com/alpic-ai/skybridge/pull/996
* Add authplaneProvider
* Chatgpt conformance failure
* Track install copies, docs CTAs and GitHub clicks in GA
* Update skills for authentication
* Allow esbuild builds for pnpm scaffolds
* Conformance usesendfollowup message false positive
* Clarify model context guidance
* Only rewrite asset URLs at runtime for JS, not CSS
* Implement skills/list and skills/get per current SEP-2640
* Scope forwarded headers to the MCP server origin
* Keep verifier messages safe inside the WWW-Authenticate challenge
* Type-safe AuthInfo.extra, owned by the verifier
* Expose thrown tool errors to MCP middleware via getToolError
* Dismiss claude modal
* DevTools: warn on large tool output and view state
* Install OpenAI Ads measurement pixel
* Preview mode with ChatGPT skeleton shell
* Claude preview shell with client switch (SKY-535)
* Read-only display mode and panel-bounded fullscreen (SKY-536)
* Mobile view in the ChatGPT and Claude previews (SKY-541)
* Add ecom template
* Document preview mode, read-only display mode and context warnings

### Contributors
@harijoe,,,,[bot],,

### v1.4.1 (Aug 24, 2026)

Fixes a crash in any tool registered without an `inputSchema`.
## v1.3: Drop In (Aug 7, 2026)

You're clipped in, the season pass is in your pocket, and the whole mountain is open. Only one thing left to do: point it downhill and drop in 🏂

`v1.2.0` was about where your apps are allowed to ride. `v1.3.0` is about *committing to the run*. You'll be getting an app from your editor to production in one click, teaching the model new tricks, and finally seeing what your view is doing when it's running somewhere you can't open a console.

- **Mixed auth, the easy way**: one `auth` field per tool to enable both public and signed-in tools on the same app
- **Deploy button**: ship straight to Alpic from the DevTools, one click, live URL in seconds
- **Skills over MCP**: serve Skills from your server as first-class MCP resources
- **View logs in terminal**: stream a view's console straight to your dev-server terminal, particularly useful when debugging issues on mobile 📱

Point your tips down the fall line. Here's what's new 🏔️

---

### Mixed auth, the easy way

`v1.1.0` gave you mixed auth by hand: `mcpAuthMetadataRouter`, `optionalBearerAuth`, and a guard written into every protected handler. `v1.2.0` gave you one-line branded providers, but they were all-or-nothing: point a server at an IdP and *every* tool sat behind sign-in.

Now the two meet. On any server with an `oauth` provider, each tool declares its own requirements with a single **`auth`** field, and the framework enforces it:

```ts
import { McpServer, descopeProvider } from "skybridge/server";
import * as z from "zod";

const server = new McpServer(
  { name: "my-app", version: "0.0.1" },
  { capabilities: {} },
  { oauth: await descopeProvider({ url: env.DESCOPE_MCP_SERVER_URL }) },
);

// Callable signed out — uses the token when one is present.
server.registerTool(
  {
    name: "search_public_docs",
    description: "Search public documents. No sign-in required.",
    inputSchema: { q: z.string() },
    auth: { allowsAnonymous: true },
  },
  async ({ q }) => ({ content: await search(q) }),
);

// Requires sign-in with the listed scopes.
server.registerTool(
  {
    name: "search_private_docs",
    description: "Search the user's private workspace.",
    inputSchema: { q: z.string() },
    auth: { scopes: ["search.read"] },
  },
  async ({ q }, extra) => ({ content: await search(q, { user: extra.authInfo }) }),
);
```

See the new `auth-descope-mixed` example for a full walkthrough.

### Deploy button

Until now, going from `skybridge dev` to a live URL meant leaving the DevTools and deploying from the CLI. We've made deploy accessible from the DevTools instead!

Hit it, sign in, and you're a few seconds away from a live deployed URL.

### Skills over MCP

Skybridge can now serve [Skills](https://agentskills.io/) straight from your server as `skill://` MCP resources. Drop a `SKILL.md` (plus any supporting files) under `src/skills/`, flip one option, and the server advertises the skills capability and serves each file over MCP:

```ts
const server = new McpServer(
  { name: "my-app", version: "0.0.1" },
  { capabilities: {} },
  { skills: true },
);
```

> **Experimental** while [SEP-2640](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640) is under review. Host support for Skills over MCP isn't widely available, as this feature hasn't officially landed in the MCP spec yet. We recommend experimenting with the feature on the Alpic playground, which supports Skills over MCP out of the box.

### View logs in terminal

When your app runs inside the ChatGPT or Claude desktop or mobile app, there's no console to pop open, so your view's logs have been out of reach. Not anymore.

Skybridge now wires Vite's [`forwardConsole`](https://vite.dev/config/server-options#server-forwardconsole) into the scaffold, so a view's console prints in the terminal running your dev server. Run through the [tunnel](https://docs.skybridge.tech/test/tunnel), and the logs from an app running on your phone show up on your laptop.

No more guessing why a view breaks!

---

* Add cursor and goose
* Add conformance app
* Enable workflow_dispatch on deploy-infra
* Skip vite build for view-less MCP servers
* Deploy button in devtools
* Fix broken lockfile during merge
* Add a --plain flag to dev for piping logs to a formatter
* Bump all non-major dependencies + esbuild ^0.28
* Enable renovate automerge, reduce release-age window to 3 days
* Listed Descope as an auth provider to README
* Add Supabase Triplog example
* Update all non-major dependencies by[bot] in https://github.com/alpic-ai/skybridge/pull/635
* Rebase renovate PRs when behind main so automerge can arm
* Update actions/upload-pages-artifact action to v5 by[bot] in https://github.com/alpic-ai/skybridge/pull/926
* Update actions/configure-pages action to v6 by[bot] in https://github.com/alpic-ai/skybridge/pull/925
* Update actions/cache action to v6 by[bot] in https://github.com/alpic-ai/skybridge/pull/920
* Update actions/checkout action to v7 by[bot] in https://github.com/alpic-ai/skybridge/pull/921
* Stop renovate bumping engines.node in published packages
* Update node.js to v24 by[bot] in https://github.com/alpic-ai/skybridge/pull/753
* Update dependency typescript to v6 by[bot] in https://github.com/alpic-ai/skybridge/pull/928
* Added the token warning threshold of 4000 tokens
* Add chatgpt-files
* Dark mode example
* Stop widget e2e flake and surface flakes in CI
* Update all non-major dependencies to ^4.1.10 by[bot] in https://github.com/alpic-ai/skybridge/pull/940
* Update all non-major dependencies to ^1.148.0 by[bot] in https://github.com/alpic-ai/skybridge/pull/944
* Run on v0 and v1 branches
* Update all non-major dependencies by[bot] in https://github.com/alpic-ai/skybridge/pull/946
* Deploy button QA fixes
* Add descope.skybridge.tech CNAME for auth-descope example
* Update all non-major dependencies by[bot] in https://github.com/alpic-ai/skybridge/pull/948
* Add automation to conformance
* Conformance useRequestModal
* Update actions/upload-artifact action to v7 by[bot] in https://github.com/alpic-ai/skybridge/pull/955
* Update actions/checkout action to v7 by[bot] in https://github.com/alpic-ai/skybridge/pull/954
* Replace dependency framer-motion with motion ^12.42.2 by[bot] in https://github.com/alpic-ai/skybridge/pull/958
* Update all non-major dependencies by[bot] in https://github.com/alpic-ai/skybridge/pull/956
* Update all non-major dependencies by[bot] in https://github.com/alpic-ai/skybridge/pull/961
* Serve Agent Skills over MCP (SEP-2640)
* Update all non-major dependencies to ^1.152.0 by[bot] in https://github.com/alpic-ai/skybridge/pull/962
* Update all non-major dependencies by[bot] in https://github.com/alpic-ai/skybridge/pull/966
* Update dependency postcss to ^8.5.17 by[bot] in https://github.com/alpic-ai/skybridge/pull/969
* Disable conformance
* Add changelog page generated from github releases
* Fix README drift from example code
* Update all non-major dependencies by[bot] in https://github.com/alpic-ai/skybridge/pull/972
* Update dependency tsx to ^4.23.1 by[bot] in https://github.com/alpic-ai/skybridge/pull/973
* Update all non-major dependencies by[bot] in https://github.com/alpic-ai/skybridge/pull/975
* Update dependency mintlify to ^4.2.694 by[bot] in https://github.com/alpic-ai/skybridge/pull/977
* Update all non-major dependencies by[bot] in https://github.com/alpic-ai/skybridge/pull/980
* Update all non-major dependencies by[bot] in https://github.com/alpic-ai/skybridge/pull/981
* Add webmcp devtools setup
* Update all non-major dependencies by[bot] in https://github.com/alpic-ai/skybridge/pull/982
* Update all non-major dependencies by[bot] in https://github.com/alpic-ai/skybridge/pull/989
* Enable forwardConsole
* Mixed-auth on branded/custom providers

### Contributors
@Aayan-Ali-Hashim,,,,,,[bot],,,

### v1.3.5 (Aug 7, 2026)

Fixes the DevTools OAuth flow against a hosted authorization server (WorkOS AuthKit, Auth0, Clerk, Stytch). DevTools sent its `x-forwarded-*` headers on the cross-origin discovery, registration and token requests too, so the browser preflighted them and the flow stopped at `.well-known/openid-configuration`.

### v1.3.4 (Aug 6, 2026)

Fixes a build failure for any view whose CSS contains a `url()` (a `@font-face`, a background image).

### v1.3.3 (Aug 4, 2026)

Implements `skills/list` and `skills/get` per the current SEP-2640 revision, so hosts (starting with ChatGPT's skill importer) can discover skills. The now-removed `skill://index.json` resource is replaced by these two methods.

### v1.3.2 (Jul 29, 2026)

Fixes a bug with useSendFollowUpMessage and useFiles

### v1.3.1 (Jul 27, 2026)

Adds serverExternal to the Vite plugin, for packages that break the server bundle (e.g. bunyan's optional dtrace-provider).
## v1.2: Season Pass (Jul 10, 2026)

Up at the top, the view goes on forever. Ridge after ridge, lift after lift, all of it suddenly within reach thanks to the new season pass 🏔️

v1.1.0 got apps onto the lift. v1.2.0 is about where they're allowed to ride, this release makes **OAuth a first-class citizen**, with pre-cut trails to the major identity providers. We've also refreshed the docs so you can explore the whole resort with ease.

- **Branded OAuth providers**: WorkOS, Auth0, Clerk, Stytch, and Descope, each wired up around Dynamic Client Registration
- **Refreshed docs**: a reworked guide that's easy to navigate
- **One runtime**: views now emit a single MCP Apps resource, retiring the separate OpenAI Apps path
- **DevTools as WebMCP tools**: the model can now drive the DevTools directly through the browser's WebMCP API

---

### Branded OAuth providers

Until now, wiring identity into a Skybridge server meant doing the OAuth legwork by hand. This release makes auth a first-class concern.

Five branded providers ship ready for Dynamic Client Registration: **Auth0, Clerk, Descope, Stytch, and WorkOS**. Point one at the IdP and sign-in works out of the box. Auth is now a first-class field on `McpServer`, so it's declared once and the SDK threads it through the rest of the stack.

```ts
import { McpServer, descopeProvider } from "skybridge/server";

const server = new McpServer(
  { name: "my-app", version: "0.0.1" },
  { capabilities: {} },
  {
    oauth: await descopeProvider({
      // e.g. https://api.descope.com/v1/apps/agentic/{PROJECT_ID}/{MCP_SERVER_ID}
      url: env.DESCOPE_MCP_SERVER_URL, 
    }),
  },
);
```

For a setup without a branded provider, `customProvider` accepts any standards-compliant OAuth authorization server and Skybridge treats it like a built-in one.

```ts
import { McpServer, customProvider } from "skybridge/server";

const server = new McpServer(
  { name: "my-app", version: "0.0.1" },
  { capabilities: {} },
  {
    oauth: await customProvider({
      issuer: "https://issuer.example.com",
      // ...authorization server details
    }),
  },
);
```

Paired with the mixed-auth support from the previous release, a server now has full control over access: some tools open to everyone, the rest gated behind whichever provider is configured.

### Refreshed docs

The docs are organized as a journey. You begin by learning what MCP Apps are, what problem Skybridge solves and how to get started, then move into **Build** guides that teach each piece of an app in the order you'd actually write it, branch into **Guides** for cross-cutting topics like auth, files, and UX, and finish in **Test** and **Ship** for running and deploying. 

An **API Reference** sits alongside for exact lookups. Every page reads the same way: it names the problem, shows one complete worked example, then walks through that example piece by piece, so you learn the concept and see the real code at once.

[Explore the new docs](https://docs.skybridge.tech/)

### Unifying to a single runtime

The OpenAI Apps resource has been retired in favour of the MCP App one. This was made possible by OpenAI's support of [MCP Apps](https://modelcontextprotocol.io/extensions/apps/overview): views now always emit a single MCP Apps resource, regardless of host. The change is internal to Skybridge and has no impact on the public API.

**Note:** For the change to take effect and stop OpenAI from using the old resource, you'll need to **resubmit a new version of your app** once you've upgraded to Skybridge 1.2 and deployed the new server to production. The change is backward compatible, so there's no risk of breaking anything in your live traffic.

### DevTools as WebMCP tools

The DevTools interface is now exposed as WebMCP tools, which means the model can talk to the DevTools the same way it talks to any other view. Inspecting and driving an app during development stops being a manual click-through and becomes something the model can handle on its own.

Learn more about it in [Code with Fred #7](https://youtu.be/o_csxysfsmw?si=0JU8Yxcj8sgb_b1y)

---


- Revamp documentation
- Update discord limit
- Collapse host adaptors into single HostAdaptor with per-method routing
- Add branded DCR OAuth providers (WorkOS, Auth0, Clerk, Stytch, Descope)
- Add product hunt badges
- Add customProvider
- Add oauth field on McpServer + SDK wiring
- Restore cached outputs
- Serve view resource no matter the ?v= cache key
- Add security.md and minimumReleaseAge
- Allow configuring the built-in express.json() body parser
- Bump skybridge versions to 1.1.0[bot]
- Correct skill and devtools url
- Add capitals of the world example without map
- Change source of data
- Expose devtools interface as WebMCP tools
- Go fullscreen instead of pip
- Add chess example to showcase

### v1.2.7 (Jul 10, 2026)

Add an authorizationServer option to customProvider to advertise the AS independently of the validation issuer, and have descopeProvider pass the agentic AS (not the base-project issuer) through it.

### v1.2.6 (Jul 9, 2026)

Fix descopeProvider to comply with the new issuer format emitted by the Descope platform

### v1.2.5 (Jul 7, 2026)

Fixes apps using useRequestClose and makes the new conformance app pass

### v1.2.4 (Jul 3, 2026)

This release introduces support for the x-forwarded-prefix header, which is necessary for path-based versioning of deployments on a shared domain.

### v1.2.3 (Jul 1, 2026)

ChatGPT's implementation of `sendFollowUpMessage` under the MCP protocol leaves a lot to be desired. This patch makes sure that when the OpenAI SDK is available, it keeps using its implementation instead of the MCP one.

### v1.2.2 (Jun 30, 2026)

Docs/examples refresh with a small ViewName fix and CI release-flow fixes.

### v1.2.1 (Jun 29, 2026)

Resolves the legacy `widgets/apps-sdk` and `widgets/ext-apps` view URIs to the canonical URI, so that apps already published to the ChatGPT app store keep working after the URI scheme change.
## v1.1: First Chair (Jun 22, 2026)

Opening day at `v1.0.0` was pure chaos, and we loved it! 

A crowd flooded the lift station with their apps, and we handed a ski pass to everyone who showed up. Now the lifts are running and `v1.1.0` is ready for the ride 🚠

This release is about giving your apps more capability and an easier descent!

- **View tools**: model can now talk directly to your view instead of just interacting with the server
- **Saved inputs**: let you replay your favorite tool calls without climbing the same hill twice
- **Mixed auth**: servers let you keep the bunny slope open to everyone while gating the black diamonds behind a sign-in
- **TSDoc**: The whole public API is now documented inline, so your IDE reads like a trail map
- **Vercel deploys**: the fourth deploy target (yes, *that* one) even though it cost us a little dignity

Clip in. Here's what's new on the mountain 🏔️

---

### View tools

Until now the traffic was one-way: the model called your server, your server returned results plus a view, and the view just sat there looking pretty. For the model to interact with that view, you had to call tools on the server and either swap in a new view or update the existing one through polling or push.

With **view-provided tools** the view can register its *own* tools. These tools are handlers that run **inside the view iframe**, against the view's live state, called by the model directly, and without the additional round-trip to the server. You can register tools from your view component using the new [`useRegisterViewTool`](https://docs.skybridge.tech/api-reference/use-register-view-tool) hook.

```tsx
import { useRegisterViewTool } from "skybridge/web";
import * as z from "zod";

function Counter() {
  const [count, setCount] = useState(0);

  useRegisterViewTool(
    {
      name: "counter_increment",
      description: "Increment the on-screen counter by an amount.",
      inputSchema: { by: z.number().int().default(1) },
    },
    ({ by }) => {
      setCount((c) => c + by);
      return {
        content: [{ type: "text", text: `Counter is now ${count + by}.` }],
        structuredContent: { count: count + by },
      };
    },
  );

  return <span>{count}</span>;
}
```

The tool registers on mount, unregisters on unmount, and validates arguments against `inputSchema` before your handler ever runs. View tools are an experimental feature of MCP Apps: Alpic's playground and the MCPJam emulator are the only compatible hosts for now. Have a look at the new [`chess` example](https://github.com/alpic-ai/skybridge/tree/main/examples/chess) for a full demonstration app leveraging this feature.

### Saved input

Testing an app means calling the same tool with the same arguments over and over. The DevTools now let you **save a tool call input and replay it in one click**. Dial in the inputs once, hit save, and your favorite calls are waiting for you in the sidebar next session.

No code required: it's pure DevTools UX. The thing you save is just a tool name plus the arguments you'd otherwise retype by hand.

<img width="1296" height="720" alt="Clipboard-20260609-150415-701" src="https://github.com/user-attachments/assets/eb9e8c38-b771-4675-bc5e-4796c42e1da1" />

Pin the inputs you reach for constantly, replay them across sessions, and spend your time reading output instead of refilling in forms.

### Mixed auth

You used to pick a lane: a fully public app, or a fully authenticated one. Now you can run **both on the same server**: a few tools open to everyone, the rest gated behind sign-in. Declare each tool's requirements with `securitySchemes` so hosts can label it *before* invocation and request the right OAuth scopes, then pair it with the new [`optionalBearerAuth`](https://docs.skybridge.tech/api-reference/mcp-server#optionalbearerauth) middleware, which lets unauthenticated requests through instead of shutting everyone out.

```tsx
import { optionalBearerAuth } from "skybridge/server";
import * as z from "zod";

// Anonymous requests pass; bad tokens are still rejected.
server.use(optionalBearerAuth({ verifier }));

// Open to everyone.
server.registerTool(
  {
    name: "search_public_docs",
    description: "Search public documents. No sign-in required.",
    inputSchema: { q: z.string() },
    securitySchemes: [{ type: "noauth" }],
  },
  async ({ q }) => ({ content: await search(q) }),
);

// Requires sign-in.
server.registerTool(
  {
    name: "search_private_docs",
    description: "Search the user's private workspace.",
    inputSchema: { q: z.string() },
    securitySchemes: [{ type: "oauth2", scopes: ["search.read"] }],
  },
  async ({ q }, extra) => {
    if (!extra.authInfo) {
      return { content: "Sign in required.", isError: true };
    }
    return { content: await search(q, { user: extra.authInfo }) };
  },
);
```

The DevTools play along too: a mixed-auth server now offers **deferred sign-in**, so you can poke around in the public tools first and authenticate only when you reach for a gated one. `securitySchemes` is client-facing metadata: your handler still enforces auth, but it lets the host label each tool as public or sign-in-required before it is called.

### TSDoc everywhere

We added TSDoc comments across most of Skybridge's public API. Hover over any hook, any server method, any config field, and the docs you'd otherwise hunt for on the site show up right inside your editor: signatures, parameter notes, links, everything right where you need it. Many of you told us you want to understand the code you depend on without leaving your IDE, and we listened.

Nothing to install, nothing to configure. Upgrade and your existing imports light up.

### Vercel deploys

A new deploy target lands, bringing the count to four: **Alpic, Cloudflare, Docker, and now Vercel.** `skybridge build` emits a [Build Output API](https://vercel.com/docs/build-output-api) tree under `.vercel/output/` : a bundled serverless function, the static asset tree, and the routing config. Everything you need for you to ship in one command *without* `vercel.json`.

```bash
skybridge build
vercel deploy --prebuilt
```

While we may have more affinities for some platforms than others, we believe that diversity and flexibility across the MCP App community win any day. The more places your app can ride, the better.

---


- Use alpic UI in everything app
- Remove outer frame
- Derive success status when tool output has no input
- Add saved inputs and run button to tool header
- Add saved queries
- Notify model on UI-triggered game restart
- Use color-picking to display chess board in PiP mode
- Add chess.skybridge.tech DNS record

### v1.1.2 (Jun 22, 2026)

### Patch release

**Skybridge is launching on Product Hunt today 🚀**

To support the launch and help us reach Product of the Day, take a moment to upvote, comment, and leave a review here : https://www.producthunt.com/products/skybridge

### v1.1.1 (Jun 17, 2026)

### Patch release

#### Explanation

ChatGPT used to cache the HTML file referenced in a tool resource indefinitely. This recently changed: the cache now lasts about 30min and is then refetched. Before this patch, Skybridge only served the HTML when the exact version hash was provided as a `?v=` query param, so resubmitting an app would break view rendering in all earlier conversations once their cache expired and they refetched the old hash.

#### Fix
  
`resources/read` now resolves a view by its query-less path, so the underlying asset is served no matter what `?v=` value the consumer sends (stale cache key, mismatched version, or no param at all). The version param is only a cache-busting hint and no longer gates resolution. The consumer-facing URI is preserved on the response.
## v1.0 (Jun 2, 2026)

👨‍🍳 We cooked! Skybridge v1 is finally on the table:

**API simplification**: We ditched `registerWidget` for a unified `registerTool` entry point. In your tool config, simply set the type-safe `view.component` field, and Skybridge takes care of the rest. We also exposed the underlying Express instance so you can add non-MCP endpoints to your server.

**Devtools revamp**: Our beloved MCP inspector gets a radical makeover plus a bunch of new features:
- Plug your local env to ChatGPT and Claude with one-click **HTTP tunneling**
- Chat with your app on the **LLM playground**
- Run automated **Audit** to make sure your server is compliant with OpenAI and Anthropic store guidelines

**Production readiness**: We now support Cloudflare Workers, and we added a Dockerfile to make container deployment easy as can be.


### How to migrate?

1. Install [the latest version of the skill](https://docs.skybridge.tech/devtools/skills)
2. Run the prompt inside your favorite coding agent: `Migrate my app based on https://github.com/alpic-ai/skybridge/releases/tag/v1.0.0`

Having issues? Join [our Discord](https://discord.gg/2jc92tZQdn) to get help from the maintainers.



#### registerWidget removed — merged into registerTool (PR #701)

The separate registerWidget call is gone. Widget/view config moves into registerTool as an optional view field:

```tsx
// before
server.registerTool("my-tool", schema, handler)
server.registerWidget({ component: "my-view", ... })
```

```tsx
// after
server.registerTool(
  { name: "my-tool", ...schema, view: { component: "my-view" } },
  handler
);
```

#### mountWidget removed from public API (PR #701)

The Vite plugin now auto-mounts views found in the views directory. Any manual mountWidget call should be removed.

```tsx
// before
import { mountWidget } from "skybridge/web";

export default function SearchFlights() { ... }
mountWidget(<SearchFlights />)
```

```tsx
// after
export default function SearchFlights() { ... }
```

#### skybridge/web import path renamed to skybridge/vite (PR #720)

The Vite plugin subpath export changed:

```tsx
// before
import { skybridge } from "skybridge/web";
```

```tsx
// after
import { skybridge } from "skybridge/vite";
```

#### server/ + web/ project layout dropped (PR #701)

The old two-directory layout (src/server/, src/web/) is no longer supported. Skybridge now uses a flat src/ tree with src/views/ for view files. The views directory is configurable in the provided skybridge plugin.

```
// before
├── server/
│   └── src/
│       └── index.ts
└── web/
    └── src/
        └── widgets/
            └── my-widget.tsx
```

```
// after
└── src/
    ├── server.ts
    └── views/
        └── my-view.tsx
```

#### All widget terminology renamed to view (PR #720)

##### useWidgetState renamed to useViewState

```tsx
// before
import { useWidgetState } from "skybridge/web";
```

```tsx
// after
import { useViewState } from "skybridge/vite";
```

##### widgetsDevServer renamed to viewsDevServer

```tsx
// before
import { devtoolsStaticServer, widgetsDevServer } from "skybridge/server";
app.use(await widgetsDevServer());
```

```tsx
// after
import { devtoolsStaticServer, viewsDevServer } from "skybridge/server";
app.use(await viewsDevServer());
```

#### Removed `result` from CallToolResponse (PR #749)

As it does not exist in either mcp apps or apps sdk.


### Contributors

Thanks to everyone who contributed to Skybridge:
@harijoe,,,,,,,,,,,,,,,,,,,,,,,.[v0.36.4](https://github.com/alpic-ai/skybridge/releases/tag/untagged-40d21d5e80278f0f7a90)

### v1.0.4 (Jun 2, 2026)

### Enhancements
- Add chess example app
- Add `supabase-triplog` app
- Add `flip-coin` target call to Everything example app
- Add Vercel deployment support

### Bug fixes
- Increment port instead of random when default is taken
- Prevent widget crash when requesting a non-capital city
- Add `tsx` to blank template `devDependencies`
- Replace outdated `chatgpt-app-builder` skill with `skybridge`

### Refactoring
- Wrap user entry to hide `setViteManifest` from `src/server.ts`

### v1.0.3 (May 28, 2026)

### Enhancements
- Open tunnel URL in browser when `--tunnel` flag is used
- Redesign generated tool input form to match sidebar
- Support mixed-auth servers with deferred sign-in
- Update auth example
- Add useDownload to example app
- Add tsdocs
- Qa pass
- Make demo template pass MCP extended apps checks

### Bug fixes
- Generate .skybridge/views.d.ts before dev spawns tsc --watch
- Deploy landing on release instead of merge to main
- Relative path import in generative UI example
- Use 'pnpm run deploy' instead of 'pnpm deploy' in devtools
- Use 'pnpm run deploy' to avoid built-in pnpm deploy conflict
- Auth0 registration endpoint

### Testing
- Add e2e tests
- Add e2e test for authenticated server

### Docs
- Add demo video section between social proof and quotes

### v1.0.2 (May 21, 2026)

### v1.0.1 (May 19, 2026)
