From cc2cc1ecb6774447033b53c350a915d7c5fdd7ca Mon Sep 17 00:00:00 2001 From: Val Date: Fri, 16 Jan 2026 14:51:34 +0100 Subject: [PATCH 1/4] add .env to gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 4e1d141ac..56e4edff4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules dist -*.tsbuildinfo \ No newline at end of file +*.tsbuildinfo +.env \ No newline at end of file From 2847b4afbe508388cf1a34307570e20732cb430a Mon Sep 17 00:00:00 2001 From: Val Date: Fri, 16 Jan 2026 14:51:47 +0100 Subject: [PATCH 2/4] chore: edit vscode settings for biome autosave --- .vscode/settings.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 83f076654..05d55579c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,9 @@ { "editor.defaultFormatter": "biomejs.biome", + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports.biome": "explicit", + "source.fixAll.biome": "explicit" + }, "biome.lsp.bin": "${workspaceFolder}/node_modules/.bin/biome" } From 97af11a485000247015f502ca772a28dddd6db20 Mon Sep 17 00:00:00 2001 From: Val Date: Fri, 16 Jan 2026 14:51:55 +0100 Subject: [PATCH 3/4] doc: setup mixed auth example --- examples/mixed-auth/.env.example | 14 + examples/mixed-auth/nodemon.json | 5 + examples/mixed-auth/package.json | 45 ++ examples/mixed-auth/server/src/auth.ts | 99 ++++ examples/mixed-auth/server/src/coffee-data.ts | 229 +++++++++ examples/mixed-auth/server/src/env.ts | 16 + examples/mixed-auth/server/src/index.ts | 59 +++ examples/mixed-auth/server/src/middleware.ts | 55 ++ examples/mixed-auth/server/src/server.ts | 188 +++++++ examples/mixed-auth/tsconfig.json | 28 + examples/mixed-auth/tsconfig.server.json | 11 + examples/mixed-auth/web/src/helpers.ts | 4 + examples/mixed-auth/web/src/index.css | 426 +++++++++++++++ .../web/src/widgets/past-orders.tsx | 125 +++++ .../web/src/widgets/search-coffee-paris.tsx | 82 +++ examples/mixed-auth/web/vite.config.ts | 15 + pnpm-lock.yaml | 483 +++++++++++++++++- 17 files changed, 1882 insertions(+), 2 deletions(-) create mode 100644 examples/mixed-auth/.env.example create mode 100644 examples/mixed-auth/nodemon.json create mode 100644 examples/mixed-auth/package.json create mode 100644 examples/mixed-auth/server/src/auth.ts create mode 100644 examples/mixed-auth/server/src/coffee-data.ts create mode 100644 examples/mixed-auth/server/src/env.ts create mode 100644 examples/mixed-auth/server/src/index.ts create mode 100644 examples/mixed-auth/server/src/middleware.ts create mode 100644 examples/mixed-auth/server/src/server.ts create mode 100644 examples/mixed-auth/tsconfig.json create mode 100644 examples/mixed-auth/tsconfig.server.json create mode 100644 examples/mixed-auth/web/src/helpers.ts create mode 100644 examples/mixed-auth/web/src/index.css create mode 100644 examples/mixed-auth/web/src/widgets/past-orders.tsx create mode 100644 examples/mixed-auth/web/src/widgets/search-coffee-paris.tsx create mode 100644 examples/mixed-auth/web/vite.config.ts diff --git a/examples/mixed-auth/.env.example b/examples/mixed-auth/.env.example new file mode 100644 index 000000000..044de2d3d --- /dev/null +++ b/examples/mixed-auth/.env.example @@ -0,0 +1,14 @@ +# WorkOS AuthKit Configuration +# Get these from https://dashboard.workos.com + +# Your AuthKit domain (e.g., "yourapp.authkit.app") +AUTHKIT_DOMAIN= + +# Your WorkOS API key (starts with "sk_") +WORKOS_API_KEY= + +# Server URL (for development) +SERVER_URL=http://localhost:3000 + +# Environment +NODE_ENV=development diff --git a/examples/mixed-auth/nodemon.json b/examples/mixed-auth/nodemon.json new file mode 100644 index 000000000..57b109851 --- /dev/null +++ b/examples/mixed-auth/nodemon.json @@ -0,0 +1,5 @@ +{ + "watch": ["server/src"], + "ext": "ts,json", + "exec": "tsx server/src/index.ts" +} diff --git a/examples/mixed-auth/package.json b/examples/mixed-auth/package.json new file mode 100644 index 000000000..d6280b6e9 --- /dev/null +++ b/examples/mixed-auth/package.json @@ -0,0 +1,45 @@ +{ + "name": "skybridge-mixed-auth-example", + "version": "0.0.1", + "private": true, + "description": "Skybridge Mixed Auth Example - Coffee Shop Finder", + "type": "module", + "scripts": { + "dev": "skybridge", + "build": "skybridge build", + "start": "skybridge start", + "inspector": "mcp-inspector http://localhost:3000/mcp", + "server:build": "tsc -p tsconfig.server.json", + "server:start": "node dist/index.js", + "web:build": "tsc -b web && vite build -c web/vite.config.ts", + "web:preview": "vite preview -c web/vite.config.ts" + }, + "dependencies": { + "@modelcontextprotocol/sdk": "^1.25.1", + "@workos-inc/node": "^7.72.2", + "dotenv": "^16.5.0", + "express": "^5.2.1", + "jose": "^6.0.11", + "react": "^19.2.3", + "react-dom": "^19.2.3", + "skybridge": ">=0.22.0 <1.0.0", + "vite": "^7.3.0", + "zod": "^4.3.5" + }, + "devDependencies": { + "@modelcontextprotocol/inspector": "^0.18.0", + "@skybridge/devtools": ">=0.22.0 <1.0.0", + "@types/express": "^5.0.6", + "@types/node": "^22.15.30", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.2", + "nodemon": "^3.1.11", + "tsx": "^4.21.0", + "typescript": "^5.9.3" + }, + "workspaces": [], + "engines": { + "node": ">=24.0.0" + } +} diff --git a/examples/mixed-auth/server/src/auth.ts b/examples/mixed-auth/server/src/auth.ts new file mode 100644 index 000000000..4a3a5d372 --- /dev/null +++ b/examples/mixed-auth/server/src/auth.ts @@ -0,0 +1,99 @@ +import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js"; +import type { + ServerNotification, + ServerRequest, +} from "@modelcontextprotocol/sdk/types.js"; +import { WorkOS } from "@workos-inc/node"; +import * as jose from "jose"; +import { env } from "./env.js"; + +type Extra = RequestHandlerExtra; + +// Initialize JWKS client for AuthKit public key verification +const jwks = jose.createRemoteJWKSet( + new URL(`https://${env.AUTHKIT_DOMAIN}/oauth2/jwks`), +); + +// Initialize WorkOS client +const workos = new WorkOS(env.WORKOS_API_KEY); + +interface AuthResult { + userId: string; + email: string; + firstName: string | null; + lastName: string | null; +} + +export async function tryGetAuth( + extra: Extra, +): Promise { + const authHeader = extra.requestInfo?.headers?.authorization; + if (!authHeader) { + return undefined; + } + + const headerValue = Array.isArray(authHeader) ? authHeader[0] : authHeader; + if (!headerValue?.toLowerCase().startsWith("bearer ")) { + return undefined; + } + + const token = headerValue.slice(7).trim(); + if (!token) { + return undefined; + } + + try { + const { payload } = await jose.jwtVerify(token, jwks, { + issuer: `https://${env.AUTHKIT_DOMAIN}`, + }); + + if (!payload.sub || typeof payload.sub !== "string") { + return undefined; + } + + const user = await workos.userManagement.getUser(payload.sub); + + return { + userId: user.id, + email: user.email, + firstName: user.firstName, + lastName: user.lastName, + }; + } catch { + return undefined; + } +} + +export async function isAuthenticated(extra: Extra): Promise { + const auth = await tryGetAuth(extra); + return auth !== undefined; +} + +export async function getUserId(extra: Extra): Promise { + const auth = await tryGetAuth(extra); + return auth?.userId; +} + +export function buildWwwAuthenticateHeader( + resourceMetadataUrl: string, + scopes?: string[], +): string { + let header = `Bearer resource_metadata="${resourceMetadataUrl}"`; + if (scopes && scopes.length > 0) { + header += `, scope="${scopes.join(" ")}"`; + } + return header; +} + +export function oauthErrorResult( + wwwAuthenticateValue: string, + message: string, +) { + return { + content: [{ type: "text" as const, text: message }], + isError: true, + _meta: { + "mcp/www_authenticate": [wwwAuthenticateValue], + }, + }; +} diff --git a/examples/mixed-auth/server/src/coffee-data.ts b/examples/mixed-auth/server/src/coffee-data.ts new file mode 100644 index 000000000..404c0fd45 --- /dev/null +++ b/examples/mixed-auth/server/src/coffee-data.ts @@ -0,0 +1,229 @@ +export interface CoffeeShop { + id: string; + name: string; + neighborhood: string; + rating: number; + specialty: string; + imageUrl: string; + isFavorite?: boolean; +} + +export interface OrderItem { + name: string; + size: string; + price: number; +} + +export interface Order { + id: string; + shop: { + id: string; + name: string; + imageUrl: string; + }; + items: OrderItem[]; + total: number; + timestamp: string; + status: "delivered" | "preparing" | "cancelled"; +} + +const COFFEE_SHOPS: CoffeeShop[] = [ + { + id: "shop-1", + name: "Café de Flore", + neighborhood: "Saint-Germain-des-Prés", + rating: 4.5, + specialty: "Classic French café culture", + imageUrl: + "https://images.unsplash.com/photo-1495474472287-4d71bcdd2085?w=400", + }, + { + id: "shop-2", + name: "Coutume Café", + neighborhood: "Le Marais", + rating: 4.7, + specialty: "Specialty roasting", + imageUrl: + "https://images.unsplash.com/photo-1501339847302-ac426a4a7cbb?w=400", + }, + { + id: "shop-3", + name: "Boot Café", + neighborhood: "Le Marais", + rating: 4.4, + specialty: "Third-wave espresso", + imageUrl: + "https://images.unsplash.com/photo-1442512595331-e89e73853f31?w=400", + }, + { + id: "shop-4", + name: "Café Kitsuné", + neighborhood: "Palais Royal", + rating: 4.3, + specialty: "Japanese-inspired lattes", + imageUrl: + "https://images.unsplash.com/photo-1509042239860-f550ce710b93?w=400", + }, + { + id: "shop-5", + name: "Télescope Café", + neighborhood: "Palais Royal", + rating: 4.6, + specialty: "Filter coffee experts", + imageUrl: + "https://images.unsplash.com/photo-1498804103079-a6351b050096?w=400", + }, + { + id: "shop-6", + name: "Fragments", + neighborhood: "Le Marais", + rating: 4.5, + specialty: "Organic brunch & coffee", + imageUrl: + "https://images.unsplash.com/photo-1511920170033-f8396924c348?w=400", + }, + { + id: "shop-7", + name: "Holybelly", + neighborhood: "Canal Saint-Martin", + rating: 4.4, + specialty: "Australian-style flat whites", + imageUrl: + "https://images.unsplash.com/photo-1507133750040-4a8f57021571?w=400", + }, + { + id: "shop-8", + name: "Café Oberkampf", + neighborhood: "Oberkampf", + rating: 4.8, + specialty: "Single origin pour-overs", + imageUrl: + "https://images.unsplash.com/photo-1461023058943-07fcbe16d735?w=400", + }, +]; + +const MOCK_FAVORITES = ["shop-1", "shop-5", "shop-8"]; + +const MOCK_ORDERS: Order[] = [ + { + id: "order-1", + shop: { + id: "shop-1", + name: "Café de Flore", + imageUrl: + "https://images.unsplash.com/photo-1495474472287-4d71bcdd2085?w=100", + }, + items: [ + { name: "Café Crème", size: "Grand", price: 5.5 }, + { name: "Pain au Chocolat", size: "Regular", price: 3.5 }, + ], + total: 9.0, + timestamp: new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString(), + status: "delivered", + }, + { + id: "order-2", + shop: { + id: "shop-5", + name: "Télescope Café", + imageUrl: + "https://images.unsplash.com/photo-1498804103079-a6351b050096?w=100", + }, + items: [{ name: "Flat White", size: "Regular", price: 4.5 }], + total: 4.5, + timestamp: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(), + status: "delivered", + }, + { + id: "order-3", + shop: { + id: "shop-8", + name: "Café Oberkampf", + imageUrl: + "https://images.unsplash.com/photo-1461023058943-07fcbe16d735?w=100", + }, + items: [ + { name: "Noisette", size: "Regular", price: 3.5 }, + { name: "Tartine Avocat", size: "Regular", price: 11.0 }, + { name: "Jus d'Orange", size: "Small", price: 5.0 }, + ], + total: 19.5, + timestamp: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000).toISOString(), + status: "delivered", + }, + { + id: "order-4", + shop: { + id: "shop-2", + name: "Coutume Café", + imageUrl: + "https://images.unsplash.com/photo-1501339847302-ac426a4a7cbb?w=100", + }, + items: [{ name: "Double Espresso", size: "Double", price: 3.8 }], + total: 3.8, + timestamp: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(), + status: "delivered", + }, +]; + +export interface SearchParams { + query?: string; + minRating?: number; + userId?: string; +} + +export interface SearchResult { + shops: CoffeeShop[]; + isPersonalized: boolean; + totalCount: number; +} + +export function searchCoffeeShops(params: SearchParams): SearchResult { + const { query, minRating, userId } = params; + + let results = [...COFFEE_SHOPS]; + + if (query) { + const lowerQuery = query.toLowerCase(); + results = results.filter( + (shop) => + shop.name.toLowerCase().includes(lowerQuery) || + shop.specialty.toLowerCase().includes(lowerQuery), + ); + } + + if (minRating !== undefined) { + results = results.filter((shop) => shop.rating >= minRating); + } + + const isPersonalized = !!userId; + if (userId) { + results = results.map((shop) => ({ + ...shop, + isFavorite: MOCK_FAVORITES.includes(shop.id), + })); + + results.sort((shopA, shopB) => { + if (shopA.isFavorite && !shopB.isFavorite) { + return -1; + } + if (!shopA.isFavorite && shopB.isFavorite) { + return 1; + } + return shopB.rating - shopA.rating; + }); + } else { + results.sort((shopA, shopB) => shopB.rating - shopA.rating); + } + + return { + shops: results, + isPersonalized, + totalCount: results.length, + }; +} + +export function getPastOrders(_userId: string, limit: number = 10): Order[] { + // For demo: return mock orders for any authenticated user + return MOCK_ORDERS.slice(0, limit); +} diff --git a/examples/mixed-auth/server/src/env.ts b/examples/mixed-auth/server/src/env.ts new file mode 100644 index 000000000..6139a42c4 --- /dev/null +++ b/examples/mixed-auth/server/src/env.ts @@ -0,0 +1,16 @@ +import "dotenv/config"; + +function requireEnv(name: string): string { + const value = process.env[name]; + if (!value) { + throw new Error(`Missing required environment variable: ${name}`); + } + return value; +} + +export const env = { + NODE_ENV: (process.env.NODE_ENV as "development" | "production") || "development", + SERVER_URL: process.env.SERVER_URL || "http://localhost:3000", + AUTHKIT_DOMAIN: requireEnv("AUTHKIT_DOMAIN"), + WORKOS_API_KEY: requireEnv("WORKOS_API_KEY"), +}; diff --git a/examples/mixed-auth/server/src/index.ts b/examples/mixed-auth/server/src/index.ts new file mode 100644 index 000000000..1b9f030dd --- /dev/null +++ b/examples/mixed-auth/server/src/index.ts @@ -0,0 +1,59 @@ +import { mcpAuthMetadataRouter } from "@modelcontextprotocol/sdk/server/auth/router.js"; +import express from "express"; +import { widgetsDevServer } from "skybridge/server"; +import { env } from "./env.js"; +import { mcp } from "./middleware.js"; +import server from "./server.js"; + +const isProduction = env.NODE_ENV === "production"; + +const app = express(); + +app.use(express.json()); + +// Add OAuth metadata router for MCP authentication (WorkOS AuthKit) +app.use( + mcpAuthMetadataRouter({ + oauthMetadata: { + issuer: `https://${env.AUTHKIT_DOMAIN}`, + authorization_endpoint: `https://${env.AUTHKIT_DOMAIN}/oauth2/authorize`, + token_endpoint: `https://${env.AUTHKIT_DOMAIN}/oauth2/token`, + registration_endpoint: `https://${env.AUTHKIT_DOMAIN}/oauth2/register`, + response_types_supported: ["code"], + response_modes_supported: ["query"], + scopes_supported: ["openid", "profile", "email", "offline_access"], + grant_types_supported: ["authorization_code", "refresh_token"], + token_endpoint_auth_methods_supported: ["none"], + code_challenge_methods_supported: ["S256"], + }, + resourceServerUrl: new URL(env.SERVER_URL), + }), +); + +app.use(mcp(server)); + +if (!isProduction) { + const { devtoolsStaticServer } = await import("@skybridge/devtools"); + app.use(await devtoolsStaticServer()); + app.use(await widgetsDevServer()); +} + +const PORT = 3000; +app.listen(PORT, () => { + console.log(` +Mixed Auth Coffee Shop Server (WorkOS AuthKit) + +Server running at: ${env.SERVER_URL} + +Endpoints: + MCP: ${env.SERVER_URL}/mcp + OAuth Discovery: ${env.SERVER_URL}/.well-known/oauth-authorization-server + Protected Resource: ${env.SERVER_URL}/.well-known/oauth-protected-resource + +Tools: + - search-coffee-paris: Mixed auth (works without login, personalized with login) + - see-past-orders: Requires authentication + +${isProduction ? "Running in production mode" : "Running in development mode with HMR"} + `); +}); diff --git a/examples/mixed-auth/server/src/middleware.ts b/examples/mixed-auth/server/src/middleware.ts new file mode 100644 index 000000000..523b0c6f7 --- /dev/null +++ b/examples/mixed-auth/server/src/middleware.ts @@ -0,0 +1,55 @@ +import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import type { NextFunction, Request, Response } from "express"; +import type { McpServer } from "skybridge/server"; + +export const mcp = + (server: McpServer) => + async (req: Request, res: Response, next: NextFunction) => { + if (req.path !== "/mcp") { + return next(); + } + + if (req.method === "POST") { + try { + const transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: undefined, + }); + + res.on("close", () => { + transport.close(); + }); + + await server.connect(transport); + // Authorization header will be available via extra.requestInfo.headers + // Auth verification happens in tool handlers for mixed-auth pattern + await transport.handleRequest(req, res, req.body); + } catch (error) { + if (res.headersSent) { + return; + } + + console.error("Error handling MCP request:", error); + res.status(500).json({ + jsonrpc: "2.0", + error: { + code: -32603, + message: "Internal server error", + }, + id: null, + }); + } + } else if (req.method === "GET" || req.method === "DELETE") { + res.writeHead(405).end( + JSON.stringify({ + jsonrpc: "2.0", + error: { + code: -32000, + message: "Method not allowed.", + }, + id: null, + }), + ); + } else { + next(); + } + }; diff --git a/examples/mixed-auth/server/src/server.ts b/examples/mixed-auth/server/src/server.ts new file mode 100644 index 000000000..e2e39a501 --- /dev/null +++ b/examples/mixed-auth/server/src/server.ts @@ -0,0 +1,188 @@ +import { McpServer } from "skybridge/server"; +import * as z from "zod"; +import { + buildWwwAuthenticateHeader, + getUserId, + isAuthenticated, + oauthErrorResult, +} from "./auth.js"; +import { getPastOrders, searchCoffeeShops } from "./coffee-data.js"; +import { env } from "./env.js"; + +/** + * TODO: Mixed-auth (per-tool auth) does NOT work in ChatGPT currently. + * + * ChatGPT only supports two modes: fully authenticated OR no authentication. + * Users will NOT be prompted for authentication when calling a protected tool. + * + * The OpenAI Apps SDK documentation suggests using `securitySchemes` to define + * granular oauth2 per tool, but this is NOT yet implemented in the official + * @modelcontextprotocol/sdk. The TS SDK doesn't support `securitySchemes` on tools, + * and `securitySchemes` isn't in the MCP protocol yet (draft SEP). + * + * @see https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1488 + * + * Current behavior in ChatGPT: + * - If you connect with "No Auth": all tools work, but protected tools fail at runtime + * - If you connect with OAuth: user authenticates upfront, all tools have auth + * - There's NO way to prompt users for auth only when they call a protected tool + * + * Workaround: We return www-authenticate headers when auth is missing, but ChatGPT + * doesn't prompt users to sign in - the tool just fails. + * + * Once `securitySchemes` is in the protocol and TS SDK, tools could declare: + * - securitySchemes: [{ type: "noauth" }] for public tools + * - securitySchemes: [{ type: "oauth2", scopes: [] }] for auth-required tools + */ + +const RESOURCE_METADATA_URL = `${env.SERVER_URL}/.well-known/oauth-protected-resource`; + +const server = new McpServer( + { + name: "mixed-auth-coffee", + version: "0.0.1", + }, + { capabilities: {} }, +) + // Tool 1: Mixed auth - works without auth, enhanced with auth + // TODO: Once securitySchemes is supported, declare: [{ type: "noauth" }, { type: "oauth2" }] + .registerWidget( + "search-coffee-paris", + { + description: "Search for coffee shops in Paris", + _meta: { + ui: { + csp: { + resourceDomains: ["https://images.unsplash.com"], + }, + }, + }, + hosts: ["apps-sdk"], + }, + { + description: + "Search for coffee shops in Paris. Without authentication: shows basic results sorted by rating. With authentication: shows personalized results with your favorites highlighted and sorted first.", + 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, + }, + _meta: { + "openai/widgetAccessible": true, + }, + }, + async ({ query, minRating }, extra) => { + const hasAuth = await isAuthenticated(extra); + const userId = await getUserId(extra); + + const results = searchCoffeeShops({ query, minRating, userId }); + + return { + structuredContent: { + shops: results.shops, + isPersonalized: results.isPersonalized, + totalCount: results.totalCount, + }, + _meta: { + hasAuth, + userId, + }, + content: [ + { + type: "text", + text: hasAuth + ? `Found ${results.totalCount} coffee shops in Paris (personalized results with your favorites)` + : `Found ${results.totalCount} coffee shops in Paris. Sign in to see personalized results with your favorites.`, + }, + ], + isError: false, + }; + }, + ) + // Tool 2: OAuth required - fails without auth + // TODO: Once securitySchemes is supported, declare: [{ type: "oauth2", scopes: [] }] + // This would allow ChatGPT to prompt for sign-in BEFORE calling the tool (not working yet) + .registerWidget( + "see-past-orders", + { + description: "View your past coffee orders", + _meta: { + ui: { + csp: { + resourceDomains: ["https://images.unsplash.com"], + }, + }, + }, + hosts: ["apps-sdk"], + }, + { + description: + "View your past coffee orders. Requires authentication to access your order history.", + inputSchema: { + limit: z + .number() + .min(1) + .max(50) + .optional() + .default(10) + .describe("Maximum number of orders to return (default: 10)"), + }, + annotations: { + readOnlyHint: true, + openWorldHint: false, + destructiveHint: false, + }, + _meta: { + "openai/widgetAccessible": true, + }, + }, + async ({ limit }, extra) => { + // Check authentication - this tool REQUIRES auth + // TODO: With securitySchemes, ChatGPT would prompt for auth before calling. + // Currently, www-authenticate headers are returned but ChatGPT doesn't act on them. + const userId = await getUserId(extra); + if (!userId) { + const wwwAuth = buildWwwAuthenticateHeader(RESOURCE_METADATA_URL); + return { + ...oauthErrorResult( + wwwAuth, + "Authentication required to view your past orders. Please sign in to continue.", + ), + structuredContent: { requiresAuth: true }, + }; + } + + const orders = getPastOrders(userId, limit); + + return { + structuredContent: { + orders, + totalCount: orders.length, + }, + content: [ + { + type: "text", + text: `Retrieved ${orders.length} past orders for your account.`, + }, + ], + isError: false, + }; + }, + ); + +export default server; +export type AppType = typeof server; diff --git a/examples/mixed-auth/tsconfig.json b/examples/mixed-auth/tsconfig.json new file mode 100644 index 000000000..a6ba94976 --- /dev/null +++ b/examples/mixed-auth/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "jsx": "react-jsx", + + "strict": true, + "skipLibCheck": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "verbatimModuleSyntax": true, + + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + + "noEmit": true, + + "baseUrl": ".", + "paths": { + "@/*": ["./web/src/*"] + } + }, + "include": ["server/src", "web/src", "web/vite.config.ts"], + "exclude": ["dist", "node_modules"] +} diff --git a/examples/mixed-auth/tsconfig.server.json b/examples/mixed-auth/tsconfig.server.json new file mode 100644 index 000000000..d4c1c6e4b --- /dev/null +++ b/examples/mixed-auth/tsconfig.server.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "outDir": "dist", + "sourceMap": true, + "declaration": true + }, + "include": ["server/src"], + "exclude": ["dist", "node_modules"] +} diff --git a/examples/mixed-auth/web/src/helpers.ts b/examples/mixed-auth/web/src/helpers.ts new file mode 100644 index 000000000..3a8834c3c --- /dev/null +++ b/examples/mixed-auth/web/src/helpers.ts @@ -0,0 +1,4 @@ +import { generateHelpers } from "skybridge/web"; +import type { AppType } from "../../server/src/server.js"; + +export const { useCallTool, useToolInfo } = generateHelpers(); diff --git a/examples/mixed-auth/web/src/index.css b/examples/mixed-auth/web/src/index.css new file mode 100644 index 000000000..d8b6135a3 --- /dev/null +++ b/examples/mixed-auth/web/src/index.css @@ -0,0 +1,426 @@ +@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@500;600;700&family=Inter:wght@400;500;600&display=swap'); + +:root { + /* Parisian color palette */ + --bg: #faf8f5; + --bg-surface: #ffffff; + --bg-inset: #f5f0e8; + --fg: #1a1a2e; + --fg-muted: #6b6b7b; + --border: #e8e0d4; + --accent: #2c3e50; + --accent-light: #d4a574; + --gold: #c9a66b; + --navy: #1a1a2e; + --cream: #fdfcfa; + --rose: #c17b7b; + --success: #4a7c59; + --success-bg: #e8f0e8; + --error: #a65353; + --error-bg: #f5e8e8; +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: 'Inter', system-ui, -apple-system, sans-serif; + color: var(--fg); + background: var(--bg); + line-height: 1.6; + border-radius: 16px; + -webkit-font-smoothing: antialiased; +} + +.container { + padding: 1.25rem; + min-height: 300px; + background: linear-gradient(180deg, var(--cream) 0%, var(--bg) 100%); +} + +/* Elegant header */ +.header { + display: flex; + align-items: center; + gap: 0.75rem; + margin-bottom: 1.25rem; + padding-bottom: 1rem; + border-bottom: 1px solid var(--border); +} + +.header-icon { + font-size: 1.5rem; +} + +.header-title { + font-family: 'Playfair Display', Georgia, serif; + font-size: 1.35rem; + font-weight: 600; + color: var(--navy); + letter-spacing: -0.02em; +} + +.header-badge { + margin-left: auto; + font-size: 0.7rem; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.05em; + padding: 0.35rem 0.75rem; + border-radius: 2rem; + background: var(--bg-inset); + color: var(--fg-muted); + border: 1px solid var(--border); +} + +.header-badge.personalized { + background: linear-gradient(135deg, #d4a574 0%, #c9a66b 100%); + color: #fff; + border: none; + box-shadow: 0 2px 8px rgba(201, 166, 107, 0.3); +} + +/* Auth banner with French flair */ +.auth-banner { + margin-bottom: 1.25rem; + padding: 1rem 1.25rem; + border-radius: 12px; + font-size: 0.875rem; + background: linear-gradient(135deg, var(--bg-inset) 0%, #f0e8dc 100%); + color: var(--accent); + border: 1px solid var(--border); + display: flex; + align-items: center; + gap: 0.75rem; +} + +.auth-banner::before { + content: "🥐"; + font-size: 1.25rem; +} + +/* Card grid */ +.grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); + gap: 1rem; +} + +.card { + border-radius: 12px; + overflow: hidden; + background: var(--bg-surface); + border: 1px solid var(--border); + box-shadow: 0 2px 12px rgba(26, 26, 46, 0.06); + transition: transform 0.2s ease, box-shadow 0.2s ease; +} + +.card:hover { + transform: translateY(-2px); + box-shadow: 0 8px 24px rgba(26, 26, 46, 0.1); +} + +.card-image-container { + position: relative; +} + +.card-image { + width: 100%; + height: 140px; + object-fit: cover; +} + +.card-favorite { + position: absolute; + top: 0.75rem; + right: 0.75rem; + color: var(--rose); + font-size: 1.25rem; + filter: drop-shadow(0 2px 4px rgba(0,0,0,0.2)); +} + +.card-content { + padding: 1rem; +} + +.card-header { + display: flex; + justify-content: space-between; + align-items: flex-start; +} + +.card-name { + font-family: 'Playfair Display', Georgia, serif; + font-weight: 600; + font-size: 1.05rem; + color: var(--navy); +} + +.card-rating { + display: flex; + align-items: center; + gap: 0.25rem; + font-size: 0.85rem; + background: var(--bg-inset); + padding: 0.2rem 0.5rem; + border-radius: 1rem; +} + +.card-rating-star { + color: var(--gold); +} + +.card-rating-value { + color: var(--fg); + font-weight: 500; +} + +.card-location { + display: flex; + align-items: center; + gap: 0.35rem; + margin-top: 0.5rem; + font-size: 0.8rem; + color: var(--fg-muted); +} + +.card-specialty { + margin-top: 0.75rem; + font-size: 0.8rem; + color: var(--fg-muted); + font-style: italic; +} + +/* Footer */ +.footer { + margin-top: 1.5rem; + text-align: center; + font-size: 0.8rem; + color: var(--fg-muted); + padding-top: 1rem; + border-top: 1px solid var(--border); +} + +.footer::before { + content: "☕ "; +} + +/* Centered content states */ +.centered { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 1rem; + min-height: 280px; + text-align: center; + padding: 2rem; +} + +.icon-large { + font-size: 2.5rem; +} + +.lock-icon { + width: 5rem; + height: 5rem; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, var(--bg-inset) 0%, #e8e0d4 100%); + font-size: 2rem; + border: 2px solid var(--border); +} + +.auth-title { + font-family: 'Playfair Display', Georgia, serif; + font-size: 1.25rem; + font-weight: 600; + color: var(--navy); + margin-bottom: 0.25rem; +} + +.auth-text { + font-size: 0.9rem; + color: var(--fg-muted); + max-width: 280px; +} + +/* Orders list */ +.orders-list { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.order-card { + border-radius: 12px; + overflow: hidden; + background: var(--bg-surface); + border: 1px solid var(--border); + box-shadow: 0 2px 12px rgba(26, 26, 46, 0.06); +} + +.order-header { + display: flex; + align-items: center; + gap: 0.875rem; + padding: 1rem; + background: linear-gradient(135deg, var(--cream) 0%, var(--bg-inset) 100%); + border-bottom: 1px solid var(--border); +} + +.order-shop-image { + width: 3rem; + height: 3rem; + border-radius: 8px; + object-fit: cover; + border: 2px solid var(--bg-surface); + box-shadow: 0 2px 8px rgba(0,0,0,0.1); +} + +.order-shop-info { + flex: 1; + min-width: 0; +} + +.order-shop-name { + font-family: 'Playfair Display', Georgia, serif; + font-weight: 600; + font-size: 1rem; + color: var(--navy); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.order-time { + display: flex; + align-items: center; + gap: 0.35rem; + margin-top: 0.25rem; + font-size: 0.75rem; + color: var(--fg-muted); +} + +.status-badge { + font-size: 0.7rem; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.03em; + padding: 0.3rem 0.6rem; + border-radius: 1rem; +} + +.status-badge.delivered { + background: var(--success-bg); + color: var(--success); +} + +.status-badge.preparing { + background: linear-gradient(135deg, #fff3e0 0%, #ffe0b2 100%); + color: #e65100; +} + +.status-badge.cancelled { + background: var(--error-bg); + color: var(--error); +} + +.order-items { + padding: 1rem; +} + +.order-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.5rem 0; +} + +.order-item:not(:last-of-type) { + border-bottom: 1px dashed var(--border); +} + +.order-item-name { + font-size: 0.9rem; + color: var(--fg); +} + +.order-item-size { + font-size: 0.75rem; + color: var(--fg-muted); + margin-left: 0.5rem; + font-style: italic; +} + +.order-item-price { + font-size: 0.9rem; + color: var(--fg-muted); + font-weight: 500; +} + +.order-total { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 0.75rem; + padding-top: 0.75rem; + border-top: 2px solid var(--border); +} + +.order-total-label { + font-size: 0.9rem; + font-weight: 600; + color: var(--navy); +} + +.order-total-value { + font-family: 'Playfair Display', Georgia, serif; + font-size: 1.1rem; + font-weight: 700; + color: var(--accent); +} + +/* Animations */ +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +.animate-spin { + animation: spin 1.5s ease-in-out infinite; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(12px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.animate-fade-in { + animation: fadeIn 0.4s ease-out forwards; +} + +/* Stagger animation for cards */ +.card:nth-child(1) { animation-delay: 0s; } +.card:nth-child(2) { animation-delay: 0.05s; } +.card:nth-child(3) { animation-delay: 0.1s; } +.card:nth-child(4) { animation-delay: 0.15s; } +.card:nth-child(5) { animation-delay: 0.2s; } +.card:nth-child(6) { animation-delay: 0.25s; } + +.order-card:nth-child(1) { animation-delay: 0s; } +.order-card:nth-child(2) { animation-delay: 0.08s; } +.order-card:nth-child(3) { animation-delay: 0.16s; } +.order-card:nth-child(4) { animation-delay: 0.24s; } diff --git a/examples/mixed-auth/web/src/widgets/past-orders.tsx b/examples/mixed-auth/web/src/widgets/past-orders.tsx new file mode 100644 index 000000000..c973be6c7 --- /dev/null +++ b/examples/mixed-auth/web/src/widgets/past-orders.tsx @@ -0,0 +1,125 @@ +import { mountWidget } from "skybridge/web"; +import { useToolInfo } from "../helpers.js"; +import "@/index.css"; + +function formatDate(isoString: string): string { + const date = new Date(isoString); + const now = new Date(); + const diffMs = now.getTime() - date.getTime(); + const diffHours = Math.floor(diffMs / (1000 * 60 * 60)); + const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); + + if (diffHours < 1) { + return "Just now"; + } else if (diffHours < 24) { + return `${diffHours} hour${diffHours > 1 ? "s" : ""} ago`; + } else if (diffDays === 1) { + return "Yesterday"; + } else if (diffDays < 7) { + return `${diffDays} days ago`; + } else { + return date.toLocaleDateString("en-US", { month: "short", day: "numeric" }); + } +} + +function PastOrders() { + const { output, isPending } = useToolInfo<"see-past-orders">(); + + if (isPending) { + return ( +
+ +

Loading your orders...

+
+ ); + } + + if (!output) { + return null; + } + + if ("requiresAuth" in output) { + return ( +
+
🔒
+
+

Sign In Required

+

+ Please authenticate to view your past orders +

+
+
+ ); + } + + const { orders } = output; + + if (orders.length === 0) { + return ( +
+ 📦 +

No orders yet

+
+ ); + } + + return ( +
+
+ + Your Orders + + {orders.length} order{orders.length !== 1 ? "s" : ""} + +
+ +
+ {orders.map((order) => ( +
+
+ {order.shop.name} +
+
{order.shop.name}
+
+ 🕑 + {formatDate(order.timestamp)} +
+
+ + {order.status} + +
+ +
+ {order.items.map((item, itemIndex) => ( +
+
+ {item.name} + {item.size} +
+ + ${item.price.toFixed(2)} + +
+ ))} + +
+ Total + + ${order.total.toFixed(2)} + +
+
+
+ ))} +
+
+ ); +} + +export default PastOrders; +mountWidget(); diff --git a/examples/mixed-auth/web/src/widgets/search-coffee-paris.tsx b/examples/mixed-auth/web/src/widgets/search-coffee-paris.tsx new file mode 100644 index 000000000..1686cd81d --- /dev/null +++ b/examples/mixed-auth/web/src/widgets/search-coffee-paris.tsx @@ -0,0 +1,82 @@ +import { mountWidget } from "skybridge/web"; +import { useToolInfo } from "../helpers.js"; +import "@/index.css"; + +function SearchCoffeeParis() { + const { output, isPending, isSuccess } = useToolInfo<"search-coffee-paris">(); + + if (isPending) { + return ( +
+ +

Searching coffee shops...

+
+ ); + } + + if (!isSuccess || !output) { + return ( +
+

No results found

+
+ ); + } + + const { shops, isPersonalized, totalCount } = output; + + return ( +
+
+ + Coffee Shops in Paris + + {isPersonalized ? "Personalized" : "Public Results"} + +
+ + {!isPersonalized && ( +
+ Sign in to see personalized recommendations and your favorites +
+ )} + +
+ {shops.map((shop) => ( +
+
+ {shop.name} + {shop.isFavorite && ( + + )} +
+
+
+ {shop.name} +
+ + + {shop.rating.toFixed(1)} + +
+
+
+ 📍 + {shop.neighborhood} +
+

{shop.specialty}

+
+
+ ))} +
+ +
+ Showing {shops.length} of {totalCount} coffee shops +
+
+ ); +} + +export default SearchCoffeeParis; +mountWidget(); diff --git a/examples/mixed-auth/web/vite.config.ts b/examples/mixed-auth/web/vite.config.ts new file mode 100644 index 000000000..68720f9f0 --- /dev/null +++ b/examples/mixed-auth/web/vite.config.ts @@ -0,0 +1,15 @@ +import path from "node:path"; +import react from "@vitejs/plugin-react"; +import { skybridge } from "skybridge/web"; +import { defineConfig } from "vite"; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [skybridge(), react()], + root: __dirname, + resolve: { + alias: { + "@": path.resolve(__dirname, "./src"), + }, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0e793d21f..d52681520 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -234,6 +234,70 @@ importers: specifier: ^5.9.3 version: 5.9.3 + examples/mixed-auth: + dependencies: + '@modelcontextprotocol/sdk': + specifier: ^1.25.1 + version: 1.25.2(hono@4.11.3)(zod@4.3.5) + '@workos-inc/node': + specifier: ^7.72.2 + version: 7.82.0(express@5.2.1) + dotenv: + specifier: ^16.5.0 + version: 16.6.1 + express: + specifier: ^5.2.1 + version: 5.2.1 + jose: + specifier: ^6.0.11 + version: 6.1.3 + react: + specifier: ^19.2.3 + version: 19.2.3 + react-dom: + specifier: ^19.2.3 + version: 19.2.3(react@19.2.3) + skybridge: + specifier: '>=0.22.0 <1.0.0' + version: 0.22.0(@modelcontextprotocol/sdk@1.25.2(hono@4.11.3)(zod@4.3.5))(@types/node@22.19.3)(@types/react@19.2.8)(immer@9.0.21)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(terser@5.44.1)(tsx@4.21.0)(use-sync-external-store@1.6.0(react@19.2.3))(yaml@2.8.2) + vite: + specifier: ^7.3.0 + version: 7.3.1(@types/node@22.19.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + zod: + specifier: ^4.3.5 + version: 4.3.5 + devDependencies: + '@modelcontextprotocol/inspector': + specifier: ^0.18.0 + version: 0.18.0(@types/node@22.19.3)(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(hono@4.11.3)(typescript@5.9.3) + '@skybridge/devtools': + specifier: '>=0.22.0 <1.0.0' + version: 0.22.0(@types/node@22.19.3)(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(hono@4.11.3)(immer@9.0.21)(jiti@2.6.1)(lightningcss@1.30.2)(react-router-dom@7.11.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-router@7.11.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(yaml@2.8.2)(zod@4.3.5) + '@types/express': + specifier: ^5.0.6 + version: 5.0.6 + '@types/node': + specifier: ^22.15.30 + version: 22.19.3 + '@types/react': + specifier: ^19.2.7 + version: 19.2.8 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.8) + '@vitejs/plugin-react': + specifier: ^5.1.2 + version: 5.1.2(vite@7.3.1(@types/node@22.19.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + nodemon: + specifier: ^3.1.11 + version: 3.1.11 + tsx: + specifier: ^4.21.0 + version: 4.21.0 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + packages/core: dependencies: '@babel/core': @@ -1712,6 +1776,17 @@ packages: cpu: [x64] os: [win32] + '@peculiar/asn1-schema@2.6.0': + resolution: {integrity: sha512-xNLYLBFTBKkCzEZIw842BxytQQATQv+lDTCEMZ8C196iJcJJMBUZxrhSTxLaohMyKK8QlzRNTRkUmanucnDSqg==} + + '@peculiar/json-schema@1.1.12': + resolution: {integrity: sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==} + engines: {node: '>=8.0.0'} + + '@peculiar/webcrypto@1.5.0': + resolution: {integrity: sha512-BRs5XUAwiyCDQMsVA9IDvDa7UBR9gAvPHgugOeGng3YN6vJ9JYonyDc0lNczErgtCWtucjR5N7VtaonboD/ezg==} + engines: {node: '>=10.12.0'} + '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} @@ -2628,6 +2703,9 @@ packages: '@tsconfig/node16@1.0.4': resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + '@types/accepts@1.3.7': + resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} + '@types/acorn@4.0.6': resolution: {integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==} @@ -2658,9 +2736,18 @@ packages: '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@types/content-disposition@0.5.9': + resolution: {integrity: sha512-8uYXI3Gw35MhiVYhG3s295oihrxRyytcRHjSjqnqZVDDy/xcGBRny7+Xj1Wgfhv5QzRtN2hB2dVRBUX9XW3UcQ==} + '@types/cookie@0.4.1': resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==} + '@types/cookie@0.5.4': + resolution: {integrity: sha512-7z/eR6O859gyWIAjuvBWFzNURmf2oPBmJlfVWkwehU5nzIyjwBsTh7WMmEEV4JFnHuQ3ex4oyTvfKzcyJVDBNA==} + + '@types/cookies@0.9.2': + resolution: {integrity: sha512-1AvkDdZM2dbyFybL4fxpuNCaWyv//0AwsuUk2DWeXyM1/5ZKm6W3z6mQi24RZ4l2ucY+bkSHzbDVpySqPGuV8A==} + '@types/cors@2.8.19': resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==} @@ -2679,9 +2766,15 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/express-serve-static-core@4.19.8': + resolution: {integrity: sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==} + '@types/express-serve-static-core@5.1.0': resolution: {integrity: sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==} + '@types/express@4.17.25': + resolution: {integrity: sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==} + '@types/express@5.0.6': resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==} @@ -2694,6 +2787,9 @@ packages: '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/http-assert@1.5.6': + resolution: {integrity: sha512-TTEwmtjgVbYAzZYWyeHPrrtWnfVkm8tQkP8P21uQifPgMRgjrow3XDEYqucuC8SKZJT7pUnhU/JymvjggxO9vw==} + '@types/http-cache-semantics@4.0.4': resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} @@ -2712,6 +2808,15 @@ packages: '@types/katex@0.16.8': resolution: {integrity: sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==} + '@types/keygrip@1.0.6': + resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==} + + '@types/koa-compose@3.2.9': + resolution: {integrity: sha512-BroAZ9FTvPiCy0Pi8tjD1OfJ7bgU1gQf0eR6e1Vm+JJATy9eKOG3hQMFtMciMawiSOVnLMdmUOC46s7HBhSTsA==} + + '@types/koa@2.15.0': + resolution: {integrity: sha512-7QFsywoE5URbuVnG3loe03QXuGajrnotr3gQkXcEBShORai23MePfFYdhz90FEtBBpkyIYQbVD+evKtloCgX3g==} + '@types/lodash-es@4.17.12': resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} @@ -2730,12 +2835,18 @@ packages: '@types/mdx@2.0.13': resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==} + '@types/mime@1.3.5': + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} '@types/nlcst@2.0.3': resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==} + '@types/node@17.0.45': + resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} + '@types/node@22.19.3': resolution: {integrity: sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==} @@ -2765,9 +2876,15 @@ packages: '@types/react@19.2.8': resolution: {integrity: sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==} + '@types/send@0.17.6': + resolution: {integrity: sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==} + '@types/send@1.2.0': resolution: {integrity: sha512-zBF6vZJn1IaMpg3xUF25VK3gd3l8zwE0ZLRX7dsQyQi+jp4E8mMDJNGDYnYse+bQhYwWERTxVwHpi3dMOq7RKQ==} + '@types/serve-static@1.15.10': + resolution: {integrity: sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==} + '@types/serve-static@2.2.0': resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} @@ -2902,6 +3019,10 @@ packages: '@vitest/utils@4.0.17': resolution: {integrity: sha512-RG6iy+IzQpa9SB8HAFHJ9Y+pTzI+h8553MrciN9eC6TFBErqrQaTas4vG+MVj8S4uKk8uTT2p0vgZPnTdxd96w==} + '@workos-inc/node@7.82.0': + resolution: {integrity: sha512-8h6XjIJf8nqNGYQMkWsjZ72WXMtzqrb4Azz39schXWoSRmwoK6tK+GpeAviJ9slddiJdOcp0Ht9/r+L6pGQMCg==} + engines: {node: '>=16'} + '@x0k/json-schema-merge@1.0.2': resolution: {integrity: sha512-1734qiJHNX3+cJGDMMw2yz7R+7kpbAtl5NdPs1c/0gO5kYT6s4dMbLXiIfpZNsOYhGZI3aH7FWrj4Zxz7epXNg==} @@ -3079,6 +3200,10 @@ packages: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} + asn1js@3.0.7: + resolution: {integrity: sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ==} + engines: {node: '>=12.0.0'} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -3253,6 +3378,9 @@ packages: buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + bundle-name@4.1.0: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} @@ -3764,6 +3892,10 @@ packages: dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} + dotenv@17.2.3: resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} engines: {node: '>=12'} @@ -4634,6 +4766,24 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} + iron-session@6.3.1: + resolution: {integrity: sha512-3UJ7y2vk/WomAtEySmPgM6qtYF1cZ3tXuWX5GsVX4PJXAcs5y/sV9HuSfpjKS6HkTL/OhZcTDWJNLZ7w+Erx3A==} + engines: {node: '>=12'} + peerDependencies: + express: '>=4' + koa: '>=2' + next: '>=10' + peerDependenciesMeta: + express: + optional: true + koa: + optional: true + next: + optional: true + + iron-webcrypto@0.2.8: + resolution: {integrity: sha512-YPdCvjFMOBjXaYuDj5tiHst5CEk6Xw84Jo8Y2+jzhMceclAnb3+vNPP/CTtb5fO2ZEuXEaO4N+w62Vfko757KA==} + is-alphabetical@2.0.1: resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} @@ -4896,6 +5046,9 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true + jose@5.6.3: + resolution: {integrity: sha512-1Jh//hEEwMhNYPDDLwXHa2ePWgWiFNNUadVmguAAw2IJ6sj9mNxV5tGXJNqlMkJAybF6Lgw1mISDxTePP/187g==} + jose@6.1.3: resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} @@ -4998,6 +5151,9 @@ packages: lcm@0.0.3: resolution: {integrity: sha512-TB+ZjoillV6B26Vspf9l2L/vKaRY/4ep3hahcyVkCGFgsTNRUQdc24bQeNFiZeoxH0vr5+7SfNRMQuPHv/1IrQ==} + leb@1.0.0: + resolution: {integrity: sha512-Y3c3QZfvKWHX60BVOQPhLCvVGmDYWyJEiINE3drOog6KCyN2AOwvuQQzlS3uJg1J85kzpILXIUwRXULWavir+w==} + leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} @@ -6014,6 +6170,13 @@ packages: deprecated: < 24.15.0 is no longer supported hasBin: true + pvtsutils@1.3.6: + resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} + + pvutils@1.1.5: + resolution: {integrity: sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==} + engines: {node: '>=16.0.0'} + qs@6.11.0: resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} engines: {node: '>=0.6'} @@ -6022,6 +6185,10 @@ packages: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} + qs@6.14.1: + resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==} + engines: {node: '>=0.6'} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -7333,6 +7500,9 @@ packages: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} + webcrypto-core@1.8.1: + resolution: {integrity: sha512-P+x1MvlNCXlKbLSOY4cYrdreqPG5hbzkmawbcXLKN/mf6DZW0SdNNkZ+sjwsqVkI4A4Ko2sPZmkZtCKY58w83A==} + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -9162,6 +9332,32 @@ snapshots: - typescript - utf-8-validate + '@modelcontextprotocol/inspector@0.18.0(@types/node@22.19.3)(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(hono@4.11.3)(typescript@5.9.3)': + dependencies: + '@modelcontextprotocol/inspector-cli': 0.18.0(hono@4.11.3)(zod@3.25.76) + '@modelcontextprotocol/inspector-client': 0.18.0(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(hono@4.11.3) + '@modelcontextprotocol/inspector-server': 0.18.0(hono@4.11.3) + '@modelcontextprotocol/sdk': 1.25.2(hono@4.11.3)(zod@3.25.76) + concurrently: 9.2.1 + node-fetch: 3.3.2 + open: 10.2.0 + shell-quote: 1.8.3 + spawn-rx: 5.1.2 + ts-node: 10.9.2(@types/node@22.19.3)(typescript@5.9.3) + zod: 3.25.76 + transitivePeerDependencies: + - '@cfworker/json-schema' + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - '@types/react' + - '@types/react-dom' + - bufferutil + - hono + - supports-color + - typescript + - utf-8-validate + '@modelcontextprotocol/inspector@0.18.0(@types/node@25.0.3)(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(hono@4.11.3)(typescript@5.9.3)': dependencies: '@modelcontextprotocol/inspector-cli': 0.18.0(hono@4.11.3)(zod@3.25.76) @@ -9357,6 +9553,24 @@ snapshots: '@oven/bun-windows-x64@1.3.5': optional: true + '@peculiar/asn1-schema@2.6.0': + dependencies: + asn1js: 3.0.7 + pvtsutils: 1.3.6 + tslib: 2.8.1 + + '@peculiar/json-schema@1.1.12': + dependencies: + tslib: 2.8.1 + + '@peculiar/webcrypto@1.5.0': + dependencies: + '@peculiar/asn1-schema': 2.6.0 + '@peculiar/json-schema': 1.1.12 + pvtsutils: 1.3.6 + tslib: 2.8.1 + webcrypto-core: 1.8.1 + '@polka/url@1.0.0-next.29': {} '@puppeteer/browsers@2.3.0': @@ -10927,6 +11141,15 @@ snapshots: dependencies: react: 19.2.3 + '@rjsf/core@6.1.2(@rjsf/utils@6.1.2(react@19.2.0))(react@19.2.3)': + dependencies: + '@rjsf/utils': 6.1.2(react@19.2.3) + lodash: 4.17.21 + lodash-es: 4.17.22 + markdown-to-jsx: 8.0.0(react@19.2.3) + prop-types: 15.8.1 + react: 19.2.3 + '@rjsf/core@6.1.2(@rjsf/utils@6.1.2(react@19.2.3))(react@19.2.3)': dependencies: '@rjsf/utils': 6.1.2(react@19.2.3) @@ -10936,6 +11159,37 @@ snapshots: prop-types: 15.8.1 react: 19.2.3 + '@rjsf/shadcn@6.1.2(@rjsf/core@6.1.2(@rjsf/utils@6.1.2(react@19.2.0))(react@19.2.3))(@rjsf/utils@6.1.2(react@19.2.3))(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18)': + dependencies: + '@radix-ui/react-checkbox': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-icons': 1.3.2(react@19.2.3) + '@radix-ui/react-label': 2.1.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-radio-group': 1.3.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-select': 2.2.6(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-separator': 1.1.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slider': 1.3.6(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.4(@types/react@19.2.7)(react@19.2.3) + '@react-icons/all-files': 4.1.0(react@19.2.3) + '@rjsf/core': 6.1.2(@rjsf/utils@6.1.2(react@19.2.0))(react@19.2.3) + '@rjsf/utils': 6.1.2(react@19.2.3) + class-variance-authority: 0.7.1 + clsx: 2.1.1 + cmdk: 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + lodash: 4.17.21 + lodash-es: 4.17.22 + lucide-react: 0.548.0(react@19.2.3) + react: 19.2.3 + tailwind-merge: 3.4.0 + tailwindcss-animate: 1.0.7(tailwindcss@4.1.18) + uuid: 13.0.0 + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + - react-dom + - tailwindcss + '@rjsf/shadcn@6.1.2(@rjsf/core@6.1.2(@rjsf/utils@6.1.2(react@19.2.3))(react@19.2.3))(@rjsf/utils@6.1.2(react@19.2.3))(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18)': dependencies: '@radix-ui/react-checkbox': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -11170,8 +11424,8 @@ snapshots: '@fontsource-variable/jetbrains-mono': 5.2.8 '@microlink/react-json-view': 1.27.1(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@modelcontextprotocol/sdk': 1.25.2(hono@4.11.3)(zod@4.3.5) - '@rjsf/core': 6.1.2(@rjsf/utils@6.1.2(react@19.2.3))(react@19.2.3) - '@rjsf/shadcn': 6.1.2(@rjsf/core@6.1.2(@rjsf/utils@6.1.2(react@19.2.3))(react@19.2.3))(@rjsf/utils@6.1.2(react@19.2.3))(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18) + '@rjsf/core': 6.1.2(@rjsf/utils@6.1.2(react@19.2.0))(react@19.2.3) + '@rjsf/shadcn': 6.1.2(@rjsf/core@6.1.2(@rjsf/utils@6.1.2(react@19.2.0))(react@19.2.3))(@rjsf/utils@6.1.2(react@19.2.3))(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18) '@rjsf/utils': 6.1.2(react@19.2.3) '@rjsf/validator-ajv8': 6.1.2(@rjsf/utils@6.1.2(react@19.2.3)) '@tanstack/react-query': 5.90.16(react@19.2.3) @@ -11292,6 +11546,70 @@ snapshots: - yaml - zod + '@skybridge/devtools@0.22.0(@types/node@22.19.3)(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(hono@4.11.3)(immer@9.0.21)(jiti@2.6.1)(lightningcss@1.30.2)(react-router-dom@7.11.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-router@7.11.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(yaml@2.8.2)(zod@4.3.5)': + dependencies: + '@base-ui/react': 1.0.0(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@fontsource-variable/jetbrains-mono': 5.2.8 + '@microlink/react-json-view': 1.27.1(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@modelcontextprotocol/sdk': 1.25.2(hono@4.11.3)(zod@4.3.5) + '@rjsf/core': 6.1.2(@rjsf/utils@6.1.2(react@19.2.3))(react@19.2.3) + '@rjsf/shadcn': 6.1.2(@rjsf/core@6.1.2(@rjsf/utils@6.1.2(react@19.2.3))(react@19.2.3))(@rjsf/utils@6.1.2(react@19.2.3))(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18) + '@rjsf/utils': 6.1.2(react@19.2.3) + '@rjsf/validator-ajv8': 6.1.2(@rjsf/utils@6.1.2(react@19.2.3)) + '@tanstack/react-query': 5.90.16(react@19.2.3) + ahooks: 3.9.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + class-variance-authority: 0.7.1 + clsx: 2.1.1 + cors: 2.8.5 + express: 5.2.1 + framer-motion: 12.26.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + lodash-es: 4.17.22 + lucide-react: 0.562.0(react@19.2.3) + motion: 12.26.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + nuqs: 2.8.6(react-router-dom@7.11.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-router@7.11.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-resizable-panels: 4.4.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + shadcn: 3.6.3(@types/node@22.19.3)(hono@4.11.3)(typescript@5.9.3) + shiki: 3.21.0 + skybridge: 0.22.0(@modelcontextprotocol/sdk@1.25.2(hono@4.11.3)(zod@4.3.5))(@types/node@22.19.3)(@types/react@19.2.8)(immer@9.0.21)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(terser@5.44.1)(tsx@4.21.0)(use-sync-external-store@1.6.0(react@19.2.3))(yaml@2.8.2) + tailwind-merge: 3.4.0 + tailwindcss: 4.1.18 + tailwindcss-animate: 1.0.7(tailwindcss@4.1.18) + tw-animate-css: 1.4.0 + zustand: 5.0.10(@types/react@19.2.8)(immer@9.0.21)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)) + transitivePeerDependencies: + - '@cfworker/json-schema' + - '@emotion/is-prop-valid' + - '@remix-run/react' + - '@tanstack/react-router' + - '@types/node' + - '@types/react' + - '@types/react-dom' + - babel-plugin-macros + - bufferutil + - hono + - immer + - jiti + - less + - lightningcss + - next + - react-devtools-core + - react-router + - react-router-dom + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - typescript + - use-sync-external-store + - utf-8-validate + - yaml + - zod + '@skybridge/devtools@0.22.0(@types/node@25.0.3)(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(hono@4.11.3)(immer@9.0.21)(jiti@2.6.1)(lightningcss@1.30.2)(react-router-dom@7.11.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-router@7.11.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(yaml@2.8.2)(zod@4.3.5)': dependencies: '@base-ui/react': 1.0.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -11698,6 +12016,10 @@ snapshots: '@tsconfig/node16@1.0.4': {} + '@types/accepts@1.3.7': + dependencies: + '@types/node': 22.19.3 + '@types/acorn@4.0.6': dependencies: '@types/estree': 1.0.8 @@ -11741,8 +12063,19 @@ snapshots: dependencies: '@types/node': 22.19.3 + '@types/content-disposition@0.5.9': {} + '@types/cookie@0.4.1': {} + '@types/cookie@0.5.4': {} + + '@types/cookies@0.9.2': + dependencies: + '@types/connect': 3.4.38 + '@types/express': 5.0.6 + '@types/keygrip': 1.0.6 + '@types/node': 22.19.3 + '@types/cors@2.8.19': dependencies: '@types/node': 22.19.3 @@ -11763,6 +12096,13 @@ snapshots: '@types/estree@1.0.8': {} + '@types/express-serve-static-core@4.19.8': + dependencies: + '@types/node': 22.19.3 + '@types/qs': 6.14.0 + '@types/range-parser': 1.2.7 + '@types/send': 1.2.0 + '@types/express-serve-static-core@5.1.0': dependencies: '@types/node': 22.19.3 @@ -11770,6 +12110,13 @@ snapshots: '@types/range-parser': 1.2.7 '@types/send': 1.2.0 + '@types/express@4.17.25': + dependencies: + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 4.19.8 + '@types/qs': 6.14.0 + '@types/serve-static': 1.15.10 + '@types/express@5.0.6': dependencies: '@types/body-parser': 1.19.6 @@ -11786,6 +12133,8 @@ snapshots: dependencies: '@types/unist': 3.0.3 + '@types/http-assert@1.5.6': {} + '@types/http-cache-semantics@4.0.4': {} '@types/http-errors@2.0.5': {} @@ -11802,6 +12151,23 @@ snapshots: '@types/katex@0.16.8': {} + '@types/keygrip@1.0.6': {} + + '@types/koa-compose@3.2.9': + dependencies: + '@types/koa': 2.15.0 + + '@types/koa@2.15.0': + dependencies: + '@types/accepts': 1.3.7 + '@types/content-disposition': 0.5.9 + '@types/cookies': 0.9.2 + '@types/http-assert': 1.5.6 + '@types/http-errors': 2.0.5 + '@types/keygrip': 1.0.6 + '@types/koa-compose': 3.2.9 + '@types/node': 22.19.3 + '@types/lodash-es@4.17.12': dependencies: '@types/lodash': 4.17.21 @@ -11820,12 +12186,16 @@ snapshots: '@types/mdx@2.0.13': {} + '@types/mime@1.3.5': {} + '@types/ms@2.1.0': {} '@types/nlcst@2.0.3': dependencies: '@types/unist': 3.0.3 + '@types/node@17.0.45': {} + '@types/node@22.19.3': dependencies: undici-types: 6.21.0 @@ -11860,10 +12230,21 @@ snapshots: dependencies: csstype: 3.2.3 + '@types/send@0.17.6': + dependencies: + '@types/mime': 1.3.5 + '@types/node': 22.19.3 + '@types/send@1.2.0': dependencies: '@types/node': 22.19.3 + '@types/serve-static@1.15.10': + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 22.19.3 + '@types/send': 0.17.6 + '@types/serve-static@2.2.0': dependencies: '@types/http-errors': 2.0.5 @@ -12074,6 +12455,17 @@ snapshots: '@vitest/pretty-format': 4.0.17 tinyrainbow: 3.0.3 + '@workos-inc/node@7.82.0(express@5.2.1)': + dependencies: + iron-session: 6.3.1(express@5.2.1) + jose: 5.6.3 + leb: 1.0.0 + qs: 6.14.1 + transitivePeerDependencies: + - express + - koa + - next + '@x0k/json-schema-merge@1.0.2': dependencies: '@types/json-schema': 7.0.15 @@ -12244,6 +12636,12 @@ snapshots: get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 + asn1js@3.0.7: + dependencies: + pvtsutils: 1.3.6 + pvutils: 1.1.5 + tslib: 2.8.1 + assertion-error@2.0.1: {} assign-symbols@1.0.0: {} @@ -12424,6 +12822,11 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + bundle-name@4.1.0: dependencies: run-applescript: 7.1.0 @@ -12933,6 +13336,8 @@ snapshots: dom-accessibility-api@0.5.16: {} + dotenv@16.6.1: {} + dotenv@17.2.3: {} dunder-proto@1.0.1: @@ -14233,6 +14638,22 @@ snapshots: ipaddr.js@1.9.1: {} + iron-session@6.3.1(express@5.2.1): + dependencies: + '@peculiar/webcrypto': 1.5.0 + '@types/cookie': 0.5.4 + '@types/express': 4.17.25 + '@types/koa': 2.15.0 + '@types/node': 17.0.45 + cookie: 0.5.0 + iron-webcrypto: 0.2.8 + optionalDependencies: + express: 5.2.1 + + iron-webcrypto@0.2.8: + dependencies: + buffer: 6.0.3 + is-alphabetical@2.0.1: {} is-alphanumerical@2.0.1: @@ -14453,6 +14874,8 @@ snapshots: jiti@2.6.1: {} + jose@5.6.3: {} + jose@6.1.3: {} js-cookie@3.0.5: {} @@ -14554,6 +14977,8 @@ snapshots: dependencies: gcd: 0.0.1 + leb@1.0.0: {} + leven@3.1.0: {} leven@4.1.0: {} @@ -15933,6 +16358,12 @@ snapshots: - typescript - utf-8-validate + pvtsutils@1.3.6: + dependencies: + tslib: 2.8.1 + + pvutils@1.1.5: {} + qs@6.11.0: dependencies: side-channel: 1.1.0 @@ -15941,6 +16372,10 @@ snapshots: dependencies: side-channel: 1.1.0 + qs@6.14.1: + dependencies: + side-channel: 1.1.0 + queue-microtask@1.2.3: {} quick-lru@5.1.1: {} @@ -17031,6 +17466,42 @@ snapshots: - utf-8-validate - yaml + skybridge@0.22.0(@modelcontextprotocol/sdk@1.25.2(hono@4.11.3)(zod@4.3.5))(@types/node@22.19.3)(@types/react@19.2.8)(immer@9.0.21)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(terser@5.44.1)(tsx@4.21.0)(use-sync-external-store@1.6.0(react@19.2.3))(yaml@2.8.2): + dependencies: + '@babel/core': 7.28.6 + '@modelcontextprotocol/sdk': 1.25.2(hono@4.11.3)(zod@4.3.5) + '@oclif/core': 4.8.0 + cors: 2.8.5 + dequal: 2.0.3 + es-toolkit: 1.43.0 + express: 5.2.1 + handlebars: 4.7.8 + ink: 6.6.0(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + superjson: 2.2.6 + vite: 7.3.1(@types/node@22.19.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + zustand: 5.0.10(@types/react@19.2.8)(immer@9.0.21)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)) + transitivePeerDependencies: + - '@types/node' + - '@types/react' + - bufferutil + - immer + - jiti + - less + - lightningcss + - react-devtools-core + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - use-sync-external-store + - utf-8-validate + - yaml + skybridge@0.22.0(@modelcontextprotocol/sdk@1.25.2(hono@4.11.3)(zod@4.3.5))(@types/node@25.0.3)(@types/react@19.2.7)(immer@9.0.21)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(terser@5.44.1)(tsx@4.21.0)(use-sync-external-store@1.6.0(react@19.2.3))(yaml@2.8.2): dependencies: '@babel/core': 7.28.6 @@ -18088,6 +18559,14 @@ snapshots: web-streams-polyfill@3.3.3: {} + webcrypto-core@1.8.1: + dependencies: + '@peculiar/asn1-schema': 2.6.0 + '@peculiar/json-schema': 1.1.12 + asn1js: 3.0.7 + pvtsutils: 1.3.6 + tslib: 2.8.1 + webidl-conversions@3.0.1: {} webidl-conversions@8.0.1: {} From 389e391a80680c1b9d07f582f7ea98f2c0f96b92 Mon Sep 17 00:00:00 2001 From: neil-ac Date: Wed, 21 Jan 2026 18:57:32 +0100 Subject: [PATCH 4/4] fix: add shx into dev dependencies; needed by skybridge cli --- examples/mixed-auth/package.json | 1 + pnpm-lock.yaml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/examples/mixed-auth/package.json b/examples/mixed-auth/package.json index d6280b6e9..b269658ac 100644 --- a/examples/mixed-auth/package.json +++ b/examples/mixed-auth/package.json @@ -35,6 +35,7 @@ "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^5.1.2", "nodemon": "^3.1.11", + "shx":"^0.4.0", "tsx": "^4.21.0", "typescript": "^5.9.3" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d52681520..6448a5674 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -291,6 +291,9 @@ importers: nodemon: specifier: ^3.1.11 version: 3.1.11 + shx: + specifier: ^0.4.0 + version: 0.4.0 tsx: specifier: ^4.21.0 version: 4.21.0