Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1f9cc48
feat(core)!: migrate to @modelcontextprotocol/sdk v2 with per-request…
harijoe Aug 21, 2026
da941b0
feat(devtools): migrate the MCP client to sdk v2 and harden the brows…
harijoe Aug 21, 2026
b11f098
refactor(examples,templates): move to the v2 tool-handler surface, dr…
harijoe Aug 21, 2026
5e198ed
ci: publish the v2 branch to the npm alpha tag
harijoe Aug 21, 2026
aa32f22
chore(deps): bump the MCP sdk v2 packages to stable 2.0.0
harijoe Aug 21, 2026
9af7f11
feat!: land the decided v2 breaking changes
harijoe Aug 21, 2026
c3e514c
feat(web)!: regroup the context hooks per the v2 surface
harijoe Aug 21, 2026
0b7c6e9
chore: apply the merge-gate review findings
harijoe Aug 24, 2026
ff0caf0
chore!: delete the deprecations this major retires
harijoe Aug 24, 2026
548b7b6
Update packages/core/package.json
harijoe Aug 26, 2026
0afcaf6
feat(test): evals (#1058)
harijoe Aug 28, 2026
deed804
feat!: replace the registrar with a user-defined factory via the Skyb…
harijoe Aug 28, 2026
e7c7f5f
fix(examples): use import.meta.dirname in vite configs (SKY-557)
harijoe Aug 25, 2026
78b18d5
fix(devtools): freeze preview skeletons and keep scroll position when…
harijoe Aug 25, 2026
88fab52
fix(devtools): show the installed skybridge version instead of the ap…
harijoe Aug 25, 2026
39b29b1
fix(devtools): render host modal chrome so a requested modal can be c…
harijoe Aug 25, 2026
194ce16
fix(devtools): resolve the skybridge manifest by walking up from its …
harijoe Aug 25, 2026
0de4e12
fix(devtools): finish the import.meta.dirname sweep and keep the app …
harijoe Aug 25, 2026
a653e12
revert(devtools): drop the skybridge version from the header chip (SK…
harijoe Aug 25, 2026
0a505c9
fix(devtools): keep the overlay close button below the tool panel too…
harijoe Aug 25, 2026
617d5d3
refactor(devtools): reset the modal view from the openai mock and ali…
harijoe Aug 26, 2026
3635cff
feat(devtools): mimic more host chrome in the conversation preview (S…
harijoe Aug 26, 2026
c2c0f98
fix(devtools): use the official Claude logomark in the preview switch…
harijoe Aug 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: CI
on:
pull_request:
push:
branches: [main, v0, v1]
branches: [main, v0, v1, v2]
workflow_dispatch:

jobs:
Expand Down
32 changes: 29 additions & 3 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ jobs:
echo "version=0.0.0-next.${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
echo "tag=next" >> "$GITHUB_OUTPUT"
echo "✅ Next version: 0.0.0-next.${GITHUB_SHA::7}"
elif [ "${{ github.ref }}" = "refs/heads/v2" ]; then
echo "version=2.0.0-alpha.${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
echo "tag=alpha" >> "$GITHUB_OUTPUT"
echo "✅ Alpha version: 2.0.0-alpha.${GITHUB_SHA::7}"
else
echo "version=0.0.0-dev.${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
echo "tag=dev" >> "$GITHUB_OUTPUT"
Expand All @@ -71,6 +75,10 @@ jobs:
working-directory: ./packages/core
run: pnpm version ${{ steps.version.outputs.version }} --no-git-tag-version

- name: Setting version for vite-plugin
working-directory: ./packages/vite-plugin
run: pnpm version ${{ steps.version.outputs.version }} --no-git-tag-version

- name: Setting version for create-skybridge
working-directory: ./packages/create-skybridge
run: pnpm version ${{ steps.version.outputs.version }} --no-git-tag-version
Expand All @@ -79,25 +87,39 @@ jobs:
working-directory: ./packages/devtools
run: pnpm version ${{ steps.version.outputs.version }} --no-git-tag-version

- name: Setting version for test
working-directory: ./packages/test
run: pnpm version ${{ steps.version.outputs.version }} --no-git-tag-version

- name: Publish core to npm
working-directory: ./packages/core
run: ../../scripts/publish-if-new.sh ${{ steps.version.outputs.tag }}

- name: Publish vite-plugin to npm
working-directory: ./packages/vite-plugin
run: ../../scripts/publish-if-new.sh ${{ steps.version.outputs.tag }}

# Templates use `skybridge: workspace:*` so dev/CI build against the
# local package. pnpm doesn't rewrite that when bundling the templates
# into create-skybridge, so pin it to the just-published version before
# publishing create-skybridge.
- name: Pin demo template skybridge dep for publish
working-directory: ./packages/create-skybridge/templates/demo
run: pnpm pkg set 'dependencies.skybridge=^${{ steps.version.outputs.version }}'
run: |
pnpm pkg set 'dependencies.skybridge=^${{ steps.version.outputs.version }}'
pnpm pkg set 'devDependencies.@skybridge/vite-plugin=^${{ steps.version.outputs.version }}'

- name: Pin blank template skybridge dep for publish
working-directory: ./packages/create-skybridge/templates/blank
run: pnpm pkg set 'dependencies.skybridge=^${{ steps.version.outputs.version }}'
run: |
pnpm pkg set 'dependencies.skybridge=^${{ steps.version.outputs.version }}'
pnpm pkg set 'devDependencies.@skybridge/vite-plugin=^${{ steps.version.outputs.version }}'

- name: Pin ecom template skybridge dep for publish
working-directory: ./packages/create-skybridge/templates/ecom
run: pnpm pkg set 'dependencies.skybridge=^${{ steps.version.outputs.version }}'
run: |
pnpm pkg set 'dependencies.skybridge=^${{ steps.version.outputs.version }}'
pnpm pkg set 'devDependencies.@skybridge/vite-plugin=^${{ steps.version.outputs.version }}'

- name: Publish create-skybridge to npm
working-directory: ./packages/create-skybridge
Expand All @@ -107,6 +129,10 @@ jobs:
working-directory: ./packages/devtools
run: ../../scripts/publish-if-new.sh ${{ steps.version.outputs.tag }}

- name: Publish test to npm
working-directory: ./packages/test
run: ../../scripts/publish-if-new.sh ${{ steps.version.outputs.tag }}

- name: Restore templates workspace deps
if: github.event_name == 'release'
run: git checkout -- packages/create-skybridge/templates/demo/package.json packages/create-skybridge/templates/blank/package.json packages/create-skybridge/templates/ecom/package.json
Expand Down
2 changes: 1 addition & 1 deletion docs/api-reference/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ List those packages in the Vite plugin's `serverExternal` to leave them out of t
```ts vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { skybridge } from "skybridge/vite";
import { skybridge } from "@skybridge/vite-plugin";

export default defineConfig({
plugins: [react(), skybridge({ serverExternal: ["dtrace-provider"] })],
Expand Down
3 changes: 1 addition & 2 deletions docs/test/devtools.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@ Click **preview** in the toolbar and the panel becomes a mock conversation with

- **Switch client** between ChatGPT and Claude. Each shell is measured against the real app, in light and dark, and the mode resets to inline when you switch.
- **Toggle device** to render the shell inside a 390 x 844 phone frame, with the mobile layout that client uses.
- **Display modes** follow what the selected client supports. Inline and fullscreen work everywhere, pip only on ChatGPT desktop, where it renders as a floating card. A `pip` request becomes fullscreen on ChatGPT mobile, and Claude keeps the current mode.
- **Quit preview** returns to the panel with your tool state intact.
- **Display modes** follow what the selected client supports. Inline and fullscreen work everywhere, pip only on ChatGPT desktop, where it renders as a floating card. A `pip` request becomes fullscreen on ChatGPT mobile, and Claude keeps the current mode.- **Quit preview** returns to the panel with your tool state intact.

While preview is active, the `ui/initialize` handshake carries that client's own style variables, container dimensions, and available display modes, so a view that reads the host's [theme tokens](/api-reference/use-mcp-app-context) themes itself the way it will in production.

Expand Down
1 change: 1 addition & 0 deletions e2e/conformance/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
},
"devDependencies": {
"@skybridge/devtools": "^1.2.3",
"@skybridge/vite-plugin": "workspace:*",
"@tailwindcss/vite": "^4.3.1",
"@types/node": "^24.13.2",
"@types/react": "^19.2.17",
Expand Down
2 changes: 1 addition & 1 deletion e2e/conformance/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import path from "node:path";
import { skybridge } from "@skybridge/vite-plugin";
import tailwindcss from "@tailwindcss/vite";
import react from "@vitejs/plugin-react";
import { skybridge } from "skybridge/vite";
import { defineConfig } from "vite";

// https://vite.dev/config/
Expand Down
4 changes: 2 additions & 2 deletions examples/auth-auth0/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ An example MCP app built with [Skybridge](https://docs.skybridge.tech/home): a p
- **Branded provider via `oauth:`**: Passing `oauth: await auth0Provider(...)` auto-mounts the well-known metadata endpoints and Bearer verification — no manual router
- **Personalized Results**: Authenticated users see favorites highlighted and sorted first
- **User Identity in Widgets**: Displays the signed-in user's name directly in the widget UI
- **Simplified Server Setup**: Uses [`server.run()`](https://docs.skybridge.tech/api-reference/run) and `.use()` for a single-file server with no manual Express boilerplate
- **Simplified Server Setup**: Uses [`app.run()`](https://docs.skybridge.tech/api-reference/run) and `.use()` for a single-file server with no manual Express boilerplate
- **Structured Content & Metadata**: Server passes structured data to widgets via `structuredContent`
- **Hot Module Replacement**: [Live reloading](https://docs.skybridge.tech/concepts/fast-iteration#hmr-with-vite-plugin) of widget components during development
- **Local DevTools**: [DevTools](https://docs.skybridge.tech/devtools) at `http://localhost:3000` for local testing
Expand Down Expand Up @@ -100,7 +100,7 @@ This command starts:

```
├── src/
│ ├── server.ts # Server entry: McpServer + auth0Provider auth + widget + run()
│ ├── server.ts # Skybridge app: auth0Provider auth + widget
│ ├── env.ts # Env validation
│ └── coffee-data.ts # Mock coffee shop data & search
│ ├── views/
Expand Down
1 change: 1 addition & 0 deletions examples/auth-auth0/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
},
"devDependencies": {
"@skybridge/devtools": "^1.2.3",
"@skybridge/vite-plugin": "workspace:*",
"@types/express": "^5.0.6",
"@types/node": "^22.20.0",
"@types/react": "^19.2.17",
Expand Down
3 changes: 3 additions & 0 deletions examples/auth-auth0/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { app } from "./server.js";

export default await app.run();
176 changes: 88 additions & 88 deletions examples/auth-auth0/src/server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { intentMiddleware } from "@alpic-ai/insights";
import { auth0Provider, McpServer } from "skybridge/server";
import { auth0Provider, Skybridge } from "skybridge/server";
import * as z from "zod";
import { searchCoffeeShops } from "./coffee-data.js";
import { env } from "./env.js";
Expand All @@ -20,13 +20,11 @@ const AUTH0_BASE_URL = `https://${env.AUTH0_DOMAIN}`;
* Dynamic Client Registration and register an API whose Identifier is `audience`.
*/

const server = new McpServer(
export const app = new Skybridge(
{
name: "auth-coffee",
version: "0.0.1",
},
{ capabilities: {} },
{
capabilities: {},
oauth: await auth0Provider({
domain: env.AUTH0_DOMAIN,
audience: env.AUTH0_AUDIENCE, // Auth0 API Identifier
Expand All @@ -37,91 +35,93 @@ const server = new McpServer(
scopes: ["openid", "profile", "email"],
}),
},
)
.mcpMiddleware(intentMiddleware())
.registerTool(
{
name: "search-coffee-paris",
description:
"Search for coffee shops in Paris. Shows personalized results with your favorites highlighted and sorted first. Requires authentication.",
inputSchema: {
query: z
.string()
.optional()
.describe(
"Search query (name or specialty, e.g., 'latte', 'espresso')",
),
minRating: z
.number()
.min(1)
.max(5)
.optional()
.describe("Minimum rating (1-5)"),
},
annotations: {
readOnlyHint: true,
openWorldHint: true,
destructiveHint: false,
},
view: {
component: "search-coffee-paris",
description: "Search for coffee shops in Paris",
csp: {
resourceDomains: ["https://images.unsplash.com"],
(server) =>
server
.registerTool(
{
name: "search-coffee-paris",
description:
"Search for coffee shops in Paris. Shows personalized results with your favorites highlighted and sorted first. Requires authentication.",
inputSchema: {
query: z
.string()
.optional()
.describe(
"Search query (name or specialty, e.g., 'latte', 'espresso')",
),
minRating: z
.number()
.min(1)
.max(5)
.optional()
.describe("Minimum rating (1-5)"),
},
annotations: {
readOnlyHint: true,
openWorldHint: true,
destructiveHint: false,
},
view: {
component: "search-coffee-paris",
description: "Search for coffee shops in Paris",
csp: {
resourceDomains: ["https://images.unsplash.com"],
},
},
_meta: {
"openai/widgetAccessible": true,
},
},
},
_meta: {
"openai/widgetAccessible": true,
},
},
async ({ query, minRating }, extra) => {
try {
const userInfoResponse = await fetch(`${AUTH0_BASE_URL}/userinfo`, {
headers: { Authorization: `Bearer ${extra.authInfo?.token}` },
});

const userInfo = userInfoResponse.ok
? ((await userInfoResponse.json()) as {
name?: string;
email?: string;
})
: null;
async ({ query, minRating }, extra) => {
try {
const userInfoResponse = await fetch(`${AUTH0_BASE_URL}/userinfo`, {
headers: {
Authorization: `Bearer ${extra.http?.authInfo?.token}`,
},
});

const displayName = userInfo?.name ?? "User";
const results = searchCoffeeShops({
query,
minRating,
userId: extra.authInfo?.extra?.subject ?? "anonymous",
});
const userInfo = userInfoResponse.ok
? ((await userInfoResponse.json()) as {
name?: string;
email?: string;
})
: null;

return {
structuredContent: {
shops: results.shops,
totalCount: results.totalCount,
userName: displayName,
},
content: [
{
type: "text",
text: `Found ${results.totalCount} coffee shops in Paris for ${displayName}`,
},
],
isError: false,
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Failed to search coffee shops: ${error instanceof Error ? error.message : "Unknown error"}`,
},
],
isError: true,
};
}
},
);
const displayName = userInfo?.name ?? "User";
const results = searchCoffeeShops({
query,
minRating,
userId: extra.http?.authInfo?.extra?.subject ?? "anonymous",
});

export default await server.run();
return {
structuredContent: {
shops: results.shops,
totalCount: results.totalCount,
userName: displayName,
},
content: [
{
type: "text",
text: `Found ${results.totalCount} coffee shops in Paris for ${displayName}`,
},
],
isError: false,
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Failed to search coffee shops: ${error instanceof Error ? error.message : "Unknown error"}`,
},
],
isError: true,
};
}
},
)
.mcpMiddleware(intentMiddleware()),
);

export type AppType = typeof server;
export type AppType = typeof app;
4 changes: 2 additions & 2 deletions examples/auth-auth0/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from "node:path";
import { skybridge } from "@skybridge/vite-plugin";
import react from "@vitejs/plugin-react";
import { skybridge } from "skybridge/vite";
import { defineConfig } from "vite";

// https://vite.dev/config/
Expand All @@ -14,7 +14,7 @@ export default defineConfig({
plugins: [skybridge(), react()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
"@": path.resolve(import.meta.dirname, "./src"),
},
},
});
4 changes: 2 additions & 2 deletions examples/auth-authplane/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ An example MCP app built with [Skybridge](https://docs.skybridge.tech/home): a p
- **Branded provider via `oauth:`**: Passing `oauth: await authplaneProvider(...)` auto-mounts the well-known metadata endpoints and Bearer verification — no manual router
- **Personalized Results**: Favorites are highlighted and sorted first, keyed off the `sub` claim of the verified token
- **User Identity in Widgets**: The signed-in user's identity reaches the widget through `extra.authInfo`
- **Simplified Server Setup**: Uses [`server.run()`](https://docs.skybridge.tech/api-reference/run) and `.use()` for a single-file server with no manual Express boilerplate
- **Simplified Server Setup**: Uses [`app.run()`](https://docs.skybridge.tech/api-reference/run) and `.use()` for a single-file server with no manual Express boilerplate
- **Structured Content & Metadata**: Server passes structured data to widgets via `structuredContent`
- **Hot Module Replacement**: [Live reloading](https://docs.skybridge.tech/concepts/fast-iteration#hmr-with-vite-plugin) of widget components during development
- **Local DevTools**: [DevTools](https://docs.skybridge.tech/devtools) at `http://localhost:3000` for local testing
Expand Down Expand Up @@ -80,7 +80,7 @@ This command starts:

```
├── src/
│ ├── server.ts # Server entry: McpServer + authplaneProvider auth + widget + run()
│ ├── server.ts # Skybridge app: authplaneProvider auth + widget
│ ├── env.ts # Env validation
│ └── coffee-data.ts # Mock coffee shop data & search
│ ├── views/
Expand Down
1 change: 1 addition & 0 deletions examples/auth-authplane/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
},
"devDependencies": {
"@skybridge/devtools": "^1.2.3",
"@skybridge/vite-plugin": "workspace:*",
"@types/express": "^5.0.6",
"@types/node": "^22.20.0",
"@types/react": "^19.2.17",
Expand Down
3 changes: 3 additions & 0 deletions examples/auth-authplane/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { app } from "./server.js";

export default await app.run();
Loading
Loading