Changelog

What's new in Skybridge.

Every Skybridge release, sourced directly from GitHub. New features, fixes, and breaking changes.

  1. v1.3View on GitHub →

    Drop In

    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:

    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 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:

    const server = new McpServer(
      { name: "my-app", version: "0.0.1" },
      { capabilities: {} },
      { skills: true },
    );
    

    Experimental while SEP-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 into the scaffold, so a view's console prints in the terminal running your dev server. Run through the tunnel, and the logs from an app running on your phone show up on your laptop.

    No more guessing why a view breaks!


    Show 56 merged PRs

    Loading…

  2. v1.2View on GitHub →

    Season Pass

    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.

    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.

    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

    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: 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)


    Show 18 merged PRs

    Loading…

    v1.2.7

    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.

    Show 1 merged PR
    • fix(descope): advertise agentic AS in protected-resource metadata @qchuchu (#950)
    v1.2.6

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

    Show 1 merged PR
    • fix(descope): follow discovery issuer for base-project-issuer MCP servers @qchuchu (#942)
    v1.2.5

    Fixes apps using useRequestClose and makes the new conformance app pass

    Show 1 merged PR
    v1.2.4

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

    Show 1 merged PR
    v1.2.3

    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.

    Show 6 merged PRs
    v1.2.2

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

    Show 4 merged PRs
    • feat(docs): revamp examples @paulleseute (#897)
    • fix(core): default ViewName to string when registry is empty @harijoe (#898)
    • fix(ci): publish next on main merge and fix release-day version bump @harijoe (#895)
    • feat(generative-ui): add OpenUI generative UI example @vishxrad (#884)
    v1.2.1

    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.

    Show 1 merged PR
    • fix(core): resolve legacy apps-sdk/ext-apps widget view URIs @harijoe (#896)
  3. v1.1View on GitHub →

    First Chair

    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 hook.

    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 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 middleware, which lets unauthenticated requests through instead of shutting everyone out.

    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 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.

    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.


    Show 8 merged PRs

    Loading…

    v1.1.2

    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

    Show 1 merged PR
    v1.1.1

    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.

    Show 1 merged PR
  4. v1.0View on GitHub →

    👨‍🍳 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
    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 to get help from the maintainers.

    Show changes

    Loading…

    v1.0.4

    Enhancements

    Bug fixes

    • fix(cli): increment port instead of random when default is taken (#840) @harijoe
    • fix(capitals): prevent widget crash when requesting a non-capital city (#745) @udaykakade25
    • fix: add tsx to blank template devDependencies (#845) @udaykakade25
    • fix: replace outdated chatgpt-app-builder skill with skybridge (#844) @udaykakade25

    Refactoring

    • refactor(core): wrap user entry to hide setViteManifest from src/server.ts (#832) @harijoe
    v1.0.3

    Enhancements

    Bug fixes

    • fix(core): generate .skybridge/views.d.ts before dev spawns tsc --watch @harijoe (#821)
    • fix(ci): deploy landing on release instead of merge to main @harijoe (#822)
    • fix: relative path import in generative UI example @fredericbarthelet (#819)
    • fix: use 'pnpm run deploy' instead of 'pnpm deploy' in devtools @udaykakade25 (#817)
    • fix(devtools): use 'pnpm run deploy' to avoid built-in pnpm deploy conflict @harijoe (#813)
    • fix: auth0 registration endpoint @paulleseute (#809)

    Testing

    Docs

    • docs(landing): add demo video section between social proof and quotes @harijoe (#810)
    v1.0.2
    Show 6 merged PRs
    v1.0.1
    Show 12 merged PRs