skybridge 0.0.0-dev.ba6476a → 0.0.0-dev.ba711bf

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +1 -0
  2. package/dist/cli/build-helpers.d.ts +5 -1
  3. package/dist/cli/build-helpers.js +30 -10
  4. package/dist/cli/build-helpers.js.map +1 -1
  5. package/dist/cli/build-helpers.test.js +58 -3
  6. package/dist/cli/build-helpers.test.js.map +1 -1
  7. package/dist/cli/build-steps.d.ts +2 -0
  8. package/dist/cli/build-steps.js +73 -0
  9. package/dist/cli/build-steps.js.map +1 -0
  10. package/dist/cli/build-steps.test.d.ts +1 -0
  11. package/dist/cli/build-steps.test.js +52 -0
  12. package/dist/cli/build-steps.test.js.map +1 -0
  13. package/dist/cli/header.d.ts +1 -1
  14. package/dist/cli/run-plain.d.ts +18 -0
  15. package/dist/cli/run-plain.js +89 -0
  16. package/dist/cli/run-plain.js.map +1 -0
  17. package/dist/cli/use-nodemon.d.ts +14 -0
  18. package/dist/cli/use-nodemon.js +71 -60
  19. package/dist/cli/use-nodemon.js.map +1 -1
  20. package/dist/cli/use-typescript-check.d.ts +8 -2
  21. package/dist/cli/use-typescript-check.js +70 -67
  22. package/dist/cli/use-typescript-check.js.map +1 -1
  23. package/dist/commands/build.d.ts +0 -2
  24. package/dist/commands/build.js +2 -61
  25. package/dist/commands/build.js.map +1 -1
  26. package/dist/commands/dev.d.ts +1 -0
  27. package/dist/commands/dev.js +32 -0
  28. package/dist/commands/dev.js.map +1 -1
  29. package/dist/server/auth/providers/custom.d.ts +7 -0
  30. package/dist/server/auth/providers/custom.js +20 -6
  31. package/dist/server/auth/providers/custom.js.map +1 -1
  32. package/dist/server/auth/providers/custom.test.js +53 -4
  33. package/dist/server/auth/providers/custom.test.js.map +1 -1
  34. package/dist/server/auth/providers/descope.js +5 -2
  35. package/dist/server/auth/providers/descope.js.map +1 -1
  36. package/dist/server/auth/providers/descope.test.js +39 -15
  37. package/dist/server/auth/providers/descope.test.js.map +1 -1
  38. package/dist/server/auth/security-schemes.d.ts +10 -0
  39. package/dist/server/auth/security-schemes.js +19 -10
  40. package/dist/server/auth/security-schemes.js.map +1 -1
  41. package/dist/server/auth/security-schemes.test.js +13 -3
  42. package/dist/server/auth/security-schemes.test.js.map +1 -1
  43. package/dist/server/auth/setup.d.ts +2 -1
  44. package/dist/server/auth/setup.js +23 -18
  45. package/dist/server/auth/setup.js.map +1 -1
  46. package/dist/server/auth/setup.test.js +63 -9
  47. package/dist/server/auth/setup.test.js.map +1 -1
  48. package/dist/server/auth/verify.d.ts +4 -2
  49. package/dist/server/auth/verify.js +2 -2
  50. package/dist/server/auth/verify.js.map +1 -1
  51. package/dist/server/auth/verify.test.js +17 -0
  52. package/dist/server/auth/verify.test.js.map +1 -1
  53. package/dist/server/host.d.ts +2 -0
  54. package/dist/server/host.js +11 -0
  55. package/dist/server/host.js.map +1 -0
  56. package/dist/server/host.test.d.ts +1 -0
  57. package/dist/server/host.test.js +13 -0
  58. package/dist/server/host.test.js.map +1 -0
  59. package/dist/server/index.d.ts +2 -1
  60. package/dist/server/index.js +2 -1
  61. package/dist/server/index.js.map +1 -1
  62. package/dist/server/server.d.ts +35 -5
  63. package/dist/server/server.js +105 -21
  64. package/dist/server/server.js.map +1 -1
  65. package/dist/server/skills-integration.test.d.ts +1 -0
  66. package/dist/server/skills-integration.test.js +63 -0
  67. package/dist/server/skills-integration.test.js.map +1 -0
  68. package/dist/server/skills.d.ts +23 -0
  69. package/dist/server/skills.js +178 -0
  70. package/dist/server/skills.js.map +1 -0
  71. package/dist/server/skills.test.d.ts +1 -0
  72. package/dist/server/skills.test.js +210 -0
  73. package/dist/server/skills.test.js.map +1 -0
  74. package/dist/server/view-resource-resolution.test.js +69 -1
  75. package/dist/server/view-resource-resolution.test.js.map +1 -1
  76. package/dist/server/viewsDevServer.js +4 -1
  77. package/dist/server/viewsDevServer.js.map +1 -1
  78. package/dist/test/view.test.js +3 -3
  79. package/dist/test/view.test.js.map +1 -1
  80. package/dist/web/bridges/adaptor.js +22 -2
  81. package/dist/web/bridges/adaptor.js.map +1 -1
  82. package/dist/web/bridges/adaptor.test.js +11 -0
  83. package/dist/web/bridges/adaptor.test.js.map +1 -1
  84. package/dist/web/components/modal-provider.d.ts +1 -1
  85. package/dist/web/data-llm.d.ts +1 -1
  86. package/dist/web/data-llm.test.js +8 -0
  87. package/dist/web/data-llm.test.js.map +1 -1
  88. package/dist/web/plugin/plugin.js +0 -1
  89. package/dist/web/plugin/plugin.js.map +1 -1
  90. package/package.json +21 -20
@@ -1 +1 @@
1
- {"version":3,"file":"verify.test.js","sourceRoot":"","sources":["../../../src/server/auth/verify.test.ts"],"names":[],"mappings":"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","sourcesContent":["// @vitest-environment node\nimport http from \"node:http\";\nimport * as jose from \"jose\";\nimport { afterEach, describe, expect, it } from \"vitest\";\nimport { createJwksVerifier } from \"./verify.js\";\n\nconst ISSUER = \"https://issuer.test\";\nconst AUDIENCE = \"api://default\";\n\nlet jwksServer: http.Server | undefined;\nafterEach(() => jwksServer?.close());\n\nasync function startJwks() {\n const { publicKey, privateKey } = await jose.generateKeyPair(\"RS256\");\n const jwk = {\n ...(await jose.exportJWK(publicKey)),\n kid: \"test-key\",\n alg: \"RS256\",\n use: \"sig\",\n };\n const server = http.createServer((_req, res) => {\n res.setHeader(\"content-type\", \"application/json\");\n res.end(JSON.stringify({ keys: [jwk] }));\n });\n await new Promise<void>((resolve) => server.listen(0, resolve));\n jwksServer = server;\n const port = (server.address() as { port: number }).port;\n return { privateKey, jwksUri: `http://localhost:${port}/jwks` };\n}\n\nfunction sign(\n key: CryptoKey,\n claims: Record<string, unknown>,\n opts: { issuer?: string; audience?: string; expiresAt?: string } = {},\n) {\n return new jose.SignJWT(claims)\n .setProtectedHeader({ alg: \"RS256\", kid: \"test-key\" })\n .setIssuer(opts.issuer ?? ISSUER)\n .setAudience(opts.audience ?? AUDIENCE)\n .setExpirationTime(opts.expiresAt ?? \"1h\")\n .sign(key);\n}\n\ndescribe(\"createJwksVerifier\", () => {\n it(\"verifies a valid token and maps claims to AuthInfo\", async () => {\n const { privateKey, jwksUri } = await startJwks();\n const verifier = createJwksVerifier({\n issuer: ISSUER,\n audience: AUDIENCE,\n jwksUri,\n });\n const token = await sign(privateKey, {\n client_id: \"client-1\",\n scope: \"openid email\",\n sub: \"user-1\",\n email: \"a@b.test\",\n });\n\n const auth = await verifier.verifyAccessToken(token);\n\n expect(auth.token).toBe(token);\n expect(auth.clientId).toBe(\"client-1\");\n expect(auth.scopes).toEqual([\"openid\", \"email\"]);\n expect(auth.extra?.subject).toBe(\"user-1\");\n expect(auth.extra?.email).toBe(\"a@b.test\");\n });\n\n it(\"rejects a token with the wrong audience\", async () => {\n const { privateKey, jwksUri } = await startJwks();\n const verifier = createJwksVerifier({\n issuer: ISSUER,\n audience: AUDIENCE,\n jwksUri,\n });\n const token = await sign(\n privateKey,\n { client_id: \"c\" },\n { audience: \"api://other\" },\n );\n\n await expect(verifier.verifyAccessToken(token)).rejects.toThrow(\n /Token verification failed/,\n );\n });\n\n it(\"skips the aud check when no audience is configured (e.g. Clerk)\", async () => {\n const { privateKey, jwksUri } = await startJwks();\n const verifier = createJwksVerifier({ issuer: ISSUER, jwksUri });\n // Clerk-shaped access token: no `aud` claim at all.\n const token = await new jose.SignJWT({ client_id: \"c\", sub: \"u\" })\n .setProtectedHeader({ alg: \"RS256\", kid: \"test-key\" })\n .setIssuer(ISSUER)\n .setExpirationTime(\"1h\")\n .sign(privateKey);\n\n const auth = await verifier.verifyAccessToken(token);\n expect(auth.clientId).toBe(\"c\");\n expect(auth.extra?.subject).toBe(\"u\");\n });\n\n it(\"parses array scope claims and trims extra whitespace\", async () => {\n const { privateKey, jwksUri } = await startJwks();\n const verifier = createJwksVerifier({\n issuer: ISSUER,\n audience: AUDIENCE,\n jwksUri,\n });\n\n const arrayToken = await sign(privateKey, {\n scope: [\"openid\", \"email\"],\n });\n expect((await verifier.verifyAccessToken(arrayToken)).scopes).toEqual([\n \"openid\",\n \"email\",\n ]);\n\n const messyToken = await sign(privateKey, { scope: \" openid email \" });\n expect((await verifier.verifyAccessToken(messyToken)).scopes).toEqual([\n \"openid\",\n \"email\",\n ]);\n });\n});\n"]}
1
+ {"version":3,"file":"verify.test.js","sourceRoot":"","sources":["../../../src/server/auth/verify.test.ts"],"names":[],"mappings":"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","sourcesContent":["// @vitest-environment node\nimport http from \"node:http\";\nimport * as jose from \"jose\";\nimport { afterEach, describe, expect, it } from \"vitest\";\nimport { createJwksVerifier } from \"./verify.js\";\n\nconst ISSUER = \"https://issuer.test\";\nconst AUDIENCE = \"api://default\";\n\nlet jwksServer: http.Server | undefined;\nafterEach(() => jwksServer?.close());\n\nasync function startJwks() {\n const { publicKey, privateKey } = await jose.generateKeyPair(\"RS256\");\n const jwk = {\n ...(await jose.exportJWK(publicKey)),\n kid: \"test-key\",\n alg: \"RS256\",\n use: \"sig\",\n };\n const server = http.createServer((_req, res) => {\n res.setHeader(\"content-type\", \"application/json\");\n res.end(JSON.stringify({ keys: [jwk] }));\n });\n await new Promise<void>((resolve) => server.listen(0, resolve));\n jwksServer = server;\n const port = (server.address() as { port: number }).port;\n return { privateKey, jwksUri: `http://localhost:${port}/jwks` };\n}\n\nfunction sign(\n key: CryptoKey,\n claims: Record<string, unknown>,\n opts: { issuer?: string; audience?: string; expiresAt?: string } = {},\n) {\n return new jose.SignJWT(claims)\n .setProtectedHeader({ alg: \"RS256\", kid: \"test-key\" })\n .setIssuer(opts.issuer ?? ISSUER)\n .setAudience(opts.audience ?? AUDIENCE)\n .setExpirationTime(opts.expiresAt ?? \"1h\")\n .sign(key);\n}\n\ndescribe(\"createJwksVerifier\", () => {\n it(\"accepts a token from any of several configured issuers\", async () => {\n const { privateKey, jwksUri } = await startJwks();\n const other = \"https://other-issuer.test\";\n const verifier = createJwksVerifier({\n issuer: [ISSUER, other],\n audience: AUDIENCE,\n jwksUri,\n });\n\n for (const issuer of [ISSUER, other]) {\n const token = await sign(privateKey, { sub: \"user-1\" }, { issuer });\n await expect(verifier.verifyAccessToken(token)).resolves.toMatchObject({\n extra: { subject: \"user-1\" },\n });\n }\n\n const foreign = await sign(\n privateKey,\n { sub: \"user-1\" },\n { issuer: \"https://evil.test\" },\n );\n await expect(verifier.verifyAccessToken(foreign)).rejects.toThrow(/iss/);\n });\n\n it(\"verifies a valid token and maps claims to AuthInfo\", async () => {\n const { privateKey, jwksUri } = await startJwks();\n const verifier = createJwksVerifier({\n issuer: ISSUER,\n audience: AUDIENCE,\n jwksUri,\n });\n const token = await sign(privateKey, {\n client_id: \"client-1\",\n scope: \"openid email\",\n sub: \"user-1\",\n email: \"a@b.test\",\n });\n\n const auth = await verifier.verifyAccessToken(token);\n\n expect(auth.token).toBe(token);\n expect(auth.clientId).toBe(\"client-1\");\n expect(auth.scopes).toEqual([\"openid\", \"email\"]);\n expect(auth.extra?.subject).toBe(\"user-1\");\n expect(auth.extra?.email).toBe(\"a@b.test\");\n });\n\n it(\"rejects a token with the wrong audience\", async () => {\n const { privateKey, jwksUri } = await startJwks();\n const verifier = createJwksVerifier({\n issuer: ISSUER,\n audience: AUDIENCE,\n jwksUri,\n });\n const token = await sign(\n privateKey,\n { client_id: \"c\" },\n { audience: \"api://other\" },\n );\n\n await expect(verifier.verifyAccessToken(token)).rejects.toThrow(\n /Token verification failed/,\n );\n });\n\n it(\"skips the aud check when no audience is configured (e.g. Clerk)\", async () => {\n const { privateKey, jwksUri } = await startJwks();\n const verifier = createJwksVerifier({ issuer: ISSUER, jwksUri });\n // Clerk-shaped access token: no `aud` claim at all.\n const token = await new jose.SignJWT({ client_id: \"c\", sub: \"u\" })\n .setProtectedHeader({ alg: \"RS256\", kid: \"test-key\" })\n .setIssuer(ISSUER)\n .setExpirationTime(\"1h\")\n .sign(privateKey);\n\n const auth = await verifier.verifyAccessToken(token);\n expect(auth.clientId).toBe(\"c\");\n expect(auth.extra?.subject).toBe(\"u\");\n });\n\n it(\"parses array scope claims and trims extra whitespace\", async () => {\n const { privateKey, jwksUri } = await startJwks();\n const verifier = createJwksVerifier({\n issuer: ISSUER,\n audience: AUDIENCE,\n jwksUri,\n });\n\n const arrayToken = await sign(privateKey, {\n scope: [\"openid\", \"email\"],\n });\n expect((await verifier.verifyAccessToken(arrayToken)).scopes).toEqual([\n \"openid\",\n \"email\",\n ]);\n\n const messyToken = await sign(privateKey, { scope: \" openid email \" });\n expect((await verifier.verifyAccessToken(messyToken)).scopes).toEqual([\n \"openid\",\n \"email\",\n ]);\n });\n});\n"]}
@@ -0,0 +1,2 @@
1
+ export type HostKind = "openai" | "claude" | "unknown";
2
+ export declare function hostFromUserAgent(userAgent: string | undefined): HostKind;
@@ -0,0 +1,11 @@
1
+ export function hostFromUserAgent(userAgent) {
2
+ const ua = (userAgent ?? "").toLowerCase();
3
+ if (ua.includes("openai") || ua.includes("chatgpt")) {
4
+ return "openai";
5
+ }
6
+ if (ua === "claude-user") {
7
+ return "claude";
8
+ }
9
+ return "unknown";
10
+ }
11
+ //# sourceMappingURL=host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.js","sourceRoot":"","sources":["../../src/server/host.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,iBAAiB,CAAC,SAA6B;IAC7D,MAAM,EAAE,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAC3C,IAAI,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACpD,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,IAAI,EAAE,KAAK,aAAa,EAAE,CAAC;QACzB,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC","sourcesContent":["export type HostKind = \"openai\" | \"claude\" | \"unknown\";\n\nexport function hostFromUserAgent(userAgent: string | undefined): HostKind {\n const ua = (userAgent ?? \"\").toLowerCase();\n if (ua.includes(\"openai\") || ua.includes(\"chatgpt\")) {\n return \"openai\";\n }\n if (ua === \"claude-user\") {\n return \"claude\";\n }\n return \"unknown\";\n}\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,13 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { hostFromUserAgent } from "./host.js";
3
+ describe("hostFromUserAgent", () => {
4
+ it("classifies known hosts case-insensitively and defaults to unknown", () => {
5
+ expect(hostFromUserAgent("openai-mcp/1.0")).toBe("openai");
6
+ expect(hostFromUserAgent("ChatGPT")).toBe("openai");
7
+ expect(hostFromUserAgent("claude-user")).toBe("claude");
8
+ expect(hostFromUserAgent("Claude-User/1.0")).toBe("unknown");
9
+ expect(hostFromUserAgent("Mozilla/5.0")).toBe("unknown");
10
+ expect(hostFromUserAgent(undefined)).toBe("unknown");
11
+ });
12
+ });
13
+ //# sourceMappingURL=host.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.test.js","sourceRoot":"","sources":["../../src/server/host.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE9C,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAC3E,MAAM,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3D,MAAM,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACpD,MAAM,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxD,MAAM,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC7D,MAAM,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACzD,MAAM,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { hostFromUserAgent } from \"./host.js\";\n\ndescribe(\"hostFromUserAgent\", () => {\n it(\"classifies known hosts case-insensitively and defaults to unknown\", () => {\n expect(hostFromUserAgent(\"openai-mcp/1.0\")).toBe(\"openai\");\n expect(hostFromUserAgent(\"ChatGPT\")).toBe(\"openai\");\n expect(hostFromUserAgent(\"claude-user\")).toBe(\"claude\");\n expect(hostFromUserAgent(\"Claude-User/1.0\")).toBe(\"unknown\");\n expect(hostFromUserAgent(\"Mozilla/5.0\")).toBe(\"unknown\");\n expect(hostFromUserAgent(undefined)).toBe(\"unknown\");\n });\n});\n"]}
@@ -11,5 +11,6 @@ export { FileRef } from "./file-ref.js";
11
11
  export type { AnyToolRegistry, InferTools, ToolInput, ToolNames, ToolOutput, ToolResponseMetadata, } from "./inferUtilityTypes.js";
12
12
  export type { McpExtra, McpMethodString, McpMiddlewareFilter, McpMiddlewareFn, McpResultFor, McpTypedMiddlewareFn, McpWildcard, } from "./middleware.js";
13
13
  export type { HandlerContent, JsonOptions, KnownToolMeta, McpServerTypes, SecurityScheme, SkybridgeServerOptions, ToolDef, ToolMeta, ViewConfig, ViewCsp, ViewHostType, ViewName, ViewNameRegistry, } from "./server.js";
14
- export { __setBuildManifest, McpServer, normalizeContent, } from "./server.js";
14
+ export { __setBuildManifest, __setSkillsManifest, McpServer, normalizeContent, } from "./server.js";
15
+ export { discoverSkills, type Skill, type SkillsManifest, } from "./skills.js";
15
16
  export { viewsDevServer } from "./viewsDevServer.js";
@@ -7,6 +7,7 @@ export { workosProvider } from "./auth/providers/workos.js";
7
7
  export { InvalidTokenError, mcpAuthMetadataRouter, optionalBearerAuth, requireBearerAuth, } from "./auth.js";
8
8
  export { audio, embeddedResource, image, resourceLink, text, } from "./content-helpers.js";
9
9
  export { FileRef } from "./file-ref.js";
10
- export { __setBuildManifest, McpServer, normalizeContent, } from "./server.js";
10
+ export { __setBuildManifest, __setSkillsManifest, McpServer, normalizeContent, } from "./server.js";
11
+ export { discoverSkills, } from "./skills.js";
11
12
  export { viewsDevServer } from "./viewsDevServer.js";
12
13
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAIL,iBAAiB,EACjB,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,KAAK,EACL,gBAAgB,EAChB,KAAK,EACL,YAAY,EACZ,IAAI,GACL,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAiCxC,OAAO,EACL,kBAAkB,EAClB,SAAS,EACT,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC","sourcesContent":["export type { OAuthConfig } from \"./auth/index.js\";\nexport { auth0Provider } from \"./auth/providers/auth0.js\";\nexport { clerkProvider } from \"./auth/providers/clerk.js\";\nexport { customProvider } from \"./auth/providers/custom.js\";\nexport { descopeProvider } from \"./auth/providers/descope.js\";\nexport { stytchProvider } from \"./auth/providers/stytch.js\";\nexport { workosProvider } from \"./auth/providers/workos.js\";\nexport {\n type AuthInfo,\n type AuthMetadataOptions,\n type BearerAuthMiddlewareOptions,\n InvalidTokenError,\n mcpAuthMetadataRouter,\n optionalBearerAuth,\n requireBearerAuth,\n} from \"./auth.js\";\nexport {\n audio,\n embeddedResource,\n image,\n resourceLink,\n text,\n} from \"./content-helpers.js\";\nexport { FileRef } from \"./file-ref.js\";\nexport type {\n AnyToolRegistry,\n InferTools,\n ToolInput,\n ToolNames,\n ToolOutput,\n ToolResponseMetadata,\n} from \"./inferUtilityTypes.js\";\nexport type {\n McpExtra,\n McpMethodString,\n McpMiddlewareFilter,\n McpMiddlewareFn,\n McpResultFor,\n McpTypedMiddlewareFn,\n McpWildcard,\n} from \"./middleware.js\";\nexport type {\n HandlerContent,\n JsonOptions,\n KnownToolMeta,\n McpServerTypes,\n SecurityScheme,\n SkybridgeServerOptions,\n ToolDef,\n ToolMeta,\n ViewConfig,\n ViewCsp,\n ViewHostType,\n ViewName,\n ViewNameRegistry,\n} from \"./server.js\";\nexport {\n __setBuildManifest,\n McpServer,\n normalizeContent,\n} from \"./server.js\";\nexport { viewsDevServer } from \"./viewsDevServer.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAIL,iBAAiB,EACjB,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,KAAK,EACL,gBAAgB,EAChB,KAAK,EACL,YAAY,EACZ,IAAI,GACL,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAiCxC,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,SAAS,EACT,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,cAAc,GAGf,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC","sourcesContent":["export type { OAuthConfig } from \"./auth/index.js\";\nexport { auth0Provider } from \"./auth/providers/auth0.js\";\nexport { clerkProvider } from \"./auth/providers/clerk.js\";\nexport { customProvider } from \"./auth/providers/custom.js\";\nexport { descopeProvider } from \"./auth/providers/descope.js\";\nexport { stytchProvider } from \"./auth/providers/stytch.js\";\nexport { workosProvider } from \"./auth/providers/workos.js\";\nexport {\n type AuthInfo,\n type AuthMetadataOptions,\n type BearerAuthMiddlewareOptions,\n InvalidTokenError,\n mcpAuthMetadataRouter,\n optionalBearerAuth,\n requireBearerAuth,\n} from \"./auth.js\";\nexport {\n audio,\n embeddedResource,\n image,\n resourceLink,\n text,\n} from \"./content-helpers.js\";\nexport { FileRef } from \"./file-ref.js\";\nexport type {\n AnyToolRegistry,\n InferTools,\n ToolInput,\n ToolNames,\n ToolOutput,\n ToolResponseMetadata,\n} from \"./inferUtilityTypes.js\";\nexport type {\n McpExtra,\n McpMethodString,\n McpMiddlewareFilter,\n McpMiddlewareFn,\n McpResultFor,\n McpTypedMiddlewareFn,\n McpWildcard,\n} from \"./middleware.js\";\nexport type {\n HandlerContent,\n JsonOptions,\n KnownToolMeta,\n McpServerTypes,\n SecurityScheme,\n SkybridgeServerOptions,\n ToolDef,\n ToolMeta,\n ViewConfig,\n ViewCsp,\n ViewHostType,\n ViewName,\n ViewNameRegistry,\n} from \"./server.js\";\nexport {\n __setBuildManifest,\n __setSkillsManifest,\n McpServer,\n normalizeContent,\n} from \"./server.js\";\nexport {\n discoverSkills,\n type Skill,\n type SkillsManifest,\n} from \"./skills.js\";\nexport { viewsDevServer } from \"./viewsDevServer.js\";\n"]}
@@ -7,6 +7,7 @@ import type { ContentBlock, Implementation, RequestMeta, ServerNotification, Ser
7
7
  import express, { type ErrorRequestHandler, type Express, type RequestHandler } from "express";
8
8
  import type { OAuthConfig } from "./auth/index.js";
9
9
  import type { McpExtra, McpExtraFor, McpMethodString, McpMiddlewareFilter, McpMiddlewareFn, McpResultFor, McpTypedMiddlewareFn, McpWildcard } from "./middleware.js";
10
+ import { type SkillsManifest } from "./skills.js";
10
11
  /**
11
12
  * Type marker for a registered tool — carries its input, output, and response
12
13
  * metadata shapes so views can infer types from `typeof server`.
@@ -88,8 +89,19 @@ export type SecurityScheme = {
88
89
  type: "oauth2";
89
90
  scopes?: string[];
90
91
  };
91
- export type ToolAuth = "public" | "required" | {
92
- scopes: string[];
92
+ /**
93
+ * Declarative per-tool auth. Enforced when the server has an `oauth` provider:
94
+ * anonymous or under-scoped calls are rejected before the handler runs. Omit
95
+ * `auth` entirely for the secure default (sign-in required, no specific scope).
96
+ */
97
+ export type ToolAuth = {
98
+ /**
99
+ * When `true`, the tool is callable signed out; the token is still used when
100
+ * one is present. Omit (or `false`) to require sign-in.
101
+ */
102
+ allowsAnonymous?: boolean;
103
+ /** OAuth scopes the caller's token must carry to invoke the tool. */
104
+ scopes?: string[];
93
105
  };
94
106
  /**
95
107
  * Options forwarded to the built-in `express.json()` body parser. Derived
@@ -102,6 +114,11 @@ export interface SkybridgeServerOptions {
102
114
  json?: JsonOptions;
103
115
  /** Resource-server OAuth config. When set, mounts well-known metadata and bearer auth on `/mcp`. */
104
116
  oauth?: OAuthConfig;
117
+ /**
118
+ * @experimental Serve Agent Skills from `src/skills` over MCP (SEP-2640).
119
+ * API may change.
120
+ */
121
+ skills?: boolean;
105
122
  }
106
123
  /**
107
124
  * Well-known keys recognized by host runtimes when set on a tool's `_meta`.
@@ -164,7 +181,7 @@ type ExtractMeta<T> = [Extract<T, {
164
181
  type AddTool<TTools, TName extends string, TInput extends ZodRawShapeCompat, TOutput, TResponseMetadata = unknown> = McpServer<TTools & {
165
182
  [K in TName]: ToolDef<ShapeOutput<TInput>, TOutput, TResponseMetadata>;
166
183
  }>;
167
- interface ToolConfig<TInput extends ZodRawShapeCompat | AnySchema> {
184
+ interface ToolConfigBase<TInput extends ZodRawShapeCompat | AnySchema> {
168
185
  name: string;
169
186
  title?: string;
170
187
  description?: string;
@@ -172,7 +189,17 @@ interface ToolConfig<TInput extends ZodRawShapeCompat | AnySchema> {
172
189
  outputSchema?: ZodRawShapeCompat | AnySchema;
173
190
  annotations?: ToolAnnotations;
174
191
  view?: ViewConfig;
192
+ _meta?: ToolMeta;
193
+ }
194
+ /**
195
+ * The auth face of a tool config: either the high-level `auth` shorthand or the
196
+ * low-level `securitySchemes` escape hatch, never both.
197
+ */
198
+ type ToolAuthConfig = {
175
199
  auth?: ToolAuth;
200
+ securitySchemes?: never;
201
+ } | {
202
+ auth?: never;
176
203
  /**
177
204
  * Declares which auth schemes this tool supports (e.g. `noauth`, `oauth2`).
178
205
  * Lets clients label tools that require sign-in before calling, and pass
@@ -181,8 +208,8 @@ interface ToolConfig<TInput extends ZodRawShapeCompat | AnySchema> {
181
208
  * enhanced behavior to authenticated ones.
182
209
  */
183
210
  securitySchemes?: SecurityScheme[];
184
- _meta?: ToolMeta;
185
- }
211
+ };
212
+ type ToolConfig<TInput extends ZodRawShapeCompat | AnySchema> = ToolConfigBase<TInput> & ToolAuthConfig;
186
213
  /**
187
214
  * Optional client-supplied hints attached to `params._meta` on every tool call
188
215
  * by the Apps SDK host. Hints only: never use for authorization, and tolerate
@@ -240,6 +267,7 @@ declare const McpServerBaseOmitted: new (...args: ConstructorParameters<typeof M
240
267
  export declare function __setBuildManifest(manifest: Record<string, {
241
268
  file: string;
242
269
  }>): void;
270
+ export declare function __setSkillsManifest(manifest: SkillsManifest): void;
243
271
  export declare class McpServer<TTools extends Record<string, ToolDef> = Record<never, ToolDef>> extends McpServerBaseOmitted {
244
272
  readonly $types: McpServerTypes<TTools>;
245
273
  /**
@@ -273,8 +301,10 @@ export declare class McpServer<TTools extends Record<string, ToolDef> = Record<n
273
301
  private readonly serverInfo;
274
302
  private readonly serverOptions?;
275
303
  private oauthEnabled;
304
+ private resolveResourceMetadataUrl?;
276
305
  private securitySchemesByTool;
277
306
  constructor(serverInfo: Implementation, options?: ServerOptions, skybridgeOptions?: SkybridgeServerOptions);
307
+ private setupSkills;
278
308
  /**
279
309
  * Register Express middleware on the underlying app. Mirrors `app.use` —
280
310
  * pass handlers directly or a path-prefixed handler list. Register before
@@ -6,12 +6,14 @@ import { Server as SdkServer, } from "@modelcontextprotocol/sdk/server/index.js"
6
6
  import { McpServer as McpServerBase } from "@modelcontextprotocol/sdk/server/mcp.js";
7
7
  import { mergeWith, union } from "es-toolkit";
8
8
  import express, {} from "express";
9
- import { authToSecuritySchemes, evaluateSecuritySchemes, wwwAuthenticateHeader, } from "./auth/security-schemes.js";
9
+ import { authToSecuritySchemes, evaluateSecuritySchemes, inBandChallengeResult, } from "./auth/security-schemes.js";
10
10
  import { setupOAuth } from "./auth/setup.js";
11
11
  import { createApp } from "./express.js";
12
+ import { hostFromUserAgent } from "./host.js";
12
13
  import { createMiddlewareEntry } from "./metric.js";
13
14
  import { buildMiddlewareChain, getHandlerMaps } from "./middleware.js";
14
15
  import { resolveServerOrigin } from "./requestOrigin.js";
16
+ import { discoverSkills, registerSkills, SKILLS_EXTENSION_KEY, } from "./skills.js";
15
17
  import { templateHelper } from "./templateHelper.js";
16
18
  const mergeWithUnion = (target, source) => {
17
19
  return mergeWith(target, source, (targetVal, sourceVal) => {
@@ -20,6 +22,20 @@ const mergeWithUnion = (target, source) => {
20
22
  }
21
23
  });
22
24
  };
25
+ const SKILLS_DIR = "src/skills";
26
+ /**
27
+ * Normalize an `x-forwarded-prefix` value into a leading-slash, no-trailing-slash
28
+ * path. Takes the first hop of a comma-separated proxy chain.
29
+ * "/v1/", "v1", "/v1, /internal" → "/v1"; "", "/", undefined → "".
30
+ */
31
+ function normalizeForwardedPrefix(raw) {
32
+ const firstHop = raw?.split(",")[0]?.trim() ?? "";
33
+ const trimmed = firstHop.replace(/\/+$/, "");
34
+ if (trimmed === "") {
35
+ return "";
36
+ }
37
+ return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
38
+ }
23
39
  /**
24
40
  * Drop the query string from a `ui://` view URI, leaving the bare path. The
25
41
  * `?v=` cache key is the only query we append, so a plain split is enough and
@@ -93,6 +109,47 @@ let pendingBuildManifest = null;
93
109
  export function __setBuildManifest(manifest) {
94
110
  pendingBuildManifest = manifest;
95
111
  }
112
+ let pendingSkillsManifest = null;
113
+ export function __setSkillsManifest(manifest) {
114
+ pendingSkillsManifest = manifest;
115
+ }
116
+ // Pure and `this`-free so it can run inside the `super(...)` call, before `this`
117
+ // exists — the capability must be present for the `initialize` response.
118
+ function withSkillsCapability(options, skybridgeOptions) {
119
+ if (!skybridgeOptions?.skills) {
120
+ return options;
121
+ }
122
+ return {
123
+ ...options,
124
+ capabilities: {
125
+ ...options?.capabilities,
126
+ extensions: {
127
+ ...options?.capabilities?.extensions,
128
+ [SKILLS_EXTENSION_KEY]: { directoryRead: true },
129
+ },
130
+ },
131
+ };
132
+ }
133
+ // Collapses registerTool's two overloads into a single { config, cb } shape.
134
+ // registerTool("greet", { description }, handler)
135
+ // -> { config: { name: "greet", description }, cb: handler }
136
+ // registerTool({ name: "greet", description }, handler)
137
+ // -> { config: { name: "greet", description }, cb: handler }
138
+ function normalizeRegisterToolArgs(args) {
139
+ if (typeof args[0] === "string") {
140
+ return {
141
+ config: {
142
+ name: args[0],
143
+ ...args[1],
144
+ },
145
+ cb: args[2],
146
+ };
147
+ }
148
+ return {
149
+ config: args[0],
150
+ cb: args[1],
151
+ };
152
+ }
96
153
  export class McpServer extends McpServerBaseOmitted {
97
154
  /**
98
155
  * The underlying Express app. Use this to extend the HTTP server with
@@ -125,16 +182,18 @@ export class McpServer extends McpServerBaseOmitted {
125
182
  serverInfo;
126
183
  serverOptions;
127
184
  oauthEnabled = false;
185
+ resolveResourceMetadataUrl;
128
186
  securitySchemesByTool = new Map();
129
187
  constructor(serverInfo, options, skybridgeOptions) {
130
- super(serverInfo, options);
188
+ const mergedOptions = withSkillsCapability(options, skybridgeOptions);
189
+ super(serverInfo, mergedOptions);
131
190
  this.serverInfo = serverInfo;
132
- this.serverOptions = options;
191
+ this.serverOptions = mergedOptions;
133
192
  this.express = express();
134
193
  this.express.use(express.json(skybridgeOptions?.json));
135
194
  if (skybridgeOptions?.oauth) {
136
195
  this.oauthEnabled = true;
137
- setupOAuth(this.express, skybridgeOptions.oauth, this.securitySchemesByTool);
196
+ this.resolveResourceMetadataUrl = setupOAuth(this.express, skybridgeOptions.oauth, this.securitySchemesByTool);
138
197
  }
139
198
  // Pick up the manifest if `dist/__entry.js` primed it before importing
140
199
  // user code. Consume-once: clear after the first construction so a
@@ -145,6 +204,19 @@ export class McpServer extends McpServerBaseOmitted {
145
204
  this.setViteManifest(pendingBuildManifest);
146
205
  pendingBuildManifest = null;
147
206
  }
207
+ this.setupSkills(Boolean(skybridgeOptions?.skills));
208
+ }
209
+ setupSkills(enabled) {
210
+ const manifest = pendingSkillsManifest;
211
+ pendingSkillsManifest = null;
212
+ if (!enabled) {
213
+ return;
214
+ }
215
+ const skills = manifest ?? discoverSkills(SKILLS_DIR);
216
+ if (skills.length === 0) {
217
+ console.warn(`skybridge: the "skills" option is enabled but no skills were found in "${SKILLS_DIR}". Add a <name>/SKILL.md there, or remove the option.`);
218
+ }
219
+ registerSkills(this, skills);
148
220
  }
149
221
  use(pathOrHandler, ...handlers) {
150
222
  // Branching is load-bearing: Express's `app.use` overloads can't be
@@ -253,6 +325,12 @@ export class McpServer extends McpServerBaseOmitted {
253
325
  }
254
326
  },
255
327
  };
328
+ // ChatGPT reads `securitySchemes` at the tool descriptor top level (SEP-1488,
329
+ // still Draft), but the SDK's registerTool strips unknown top-level fields, so
330
+ // it's stashed in `_meta` at registration. This restores it to the top level
331
+ // on tools/list output. Remove once SEP-1488 lands and the SDK preserves it.
332
+ // { name: "checkout", _meta: { securitySchemes: [{ type: "oauth2" }] } }
333
+ // -> { name: "checkout", _meta: {…}, securitySchemes: [{ type: "oauth2" }] }
256
334
  const toolsListSecuritySchemesEntry = {
257
335
  filter: "tools/list",
258
336
  handler: async (_req, _extra, next) => {
@@ -396,8 +474,13 @@ export class McpServer extends McpServerBaseOmitted {
396
474
  const val = headers[key];
397
475
  return Array.isArray(val) ? val[0] : val;
398
476
  };
399
- const isClaude = header("user-agent") === "Claude-User";
477
+ const isClaude = hostFromUserAgent(header("user-agent")) === "claude";
400
478
  const serverUrl = resolveServerOrigin(header);
479
+ // Path prefix the proxy routed this request under (e.g. `foo.com/v1`). Read
480
+ // per-request so one process can serve many hosts/prefixes at once: the
481
+ // origin is recovered from x-forwarded-host, the prefix from
482
+ // x-forwarded-prefix. Empty when served at the origin root.
483
+ const assetsBasePath = normalizeForwardedPrefix(header("x-forwarded-prefix"));
401
484
  const connectDomains = [serverUrl];
402
485
  if (!isProduction) {
403
486
  const wsUrl = new URL(serverUrl);
@@ -418,7 +501,7 @@ export class McpServer extends McpServerBaseOmitted {
418
501
  .slice(0, 32);
419
502
  contentMetaOverrides = { domain: `${hash}.claudemcpcontent.com` };
420
503
  }
421
- return { serverUrl, connectDomains, contentMetaOverrides };
504
+ return { serverUrl, assetsBasePath, connectDomains, contentMetaOverrides };
422
505
  }
423
506
  registerViewResources(toolName, view, toolMeta) {
424
507
  // Append a content-derived version param so hosts (e.g. ChatGPT) bust
@@ -506,17 +589,21 @@ export class McpServer extends McpServerBaseOmitted {
506
589
  this.serveLegacyAppsSdkUrl(view.component, viewUri);
507
590
  this.registerResource(name, viewUri, { description: view.description }, async (uri, extra) => {
508
591
  const isProduction = process.env.NODE_ENV === "production";
509
- const { serverUrl } = this.resolveViewRequestContext(extra);
592
+ const { serverUrl, assetsBasePath } = this.resolveViewRequestContext(extra);
593
+ // The view resolves all assets (template imports + runtime lazy chunks
594
+ // via `window.skybridge.serverUrl`) against this base, so it carries the
595
+ // proxy path prefix. CSP domains in `buildMeta` stay the bare origin.
596
+ const viewBase = `${serverUrl}${assetsBasePath}`;
510
597
  const html = isProduction
511
598
  ? templateHelper.renderProduction({
512
599
  hostType,
513
- serverUrl,
600
+ serverUrl: viewBase,
514
601
  viewFile: this.lookupViewFile(view.component),
515
602
  styleFile: this.lookupDistFile("style.css") ?? "",
516
603
  })
517
604
  : templateHelper.renderDevelopment({
518
605
  hostType,
519
- serverUrl,
606
+ serverUrl: viewBase,
520
607
  viewName: view.component,
521
608
  });
522
609
  return {
@@ -536,11 +623,12 @@ export class McpServer extends McpServerBaseOmitted {
536
623
  if (this.oauthEnabled) {
537
624
  const failure = evaluateSecuritySchemes(securitySchemes, extra.authInfo);
538
625
  if (failure) {
539
- return {
540
- isError: true,
541
- content: [{ type: "text", text: failure.description }],
542
- _meta: { "mcp/www_authenticate": [wwwAuthenticateHeader(failure)] },
626
+ const headers = extra?.requestInfo?.headers ?? {};
627
+ const header = (key) => {
628
+ const value = headers[key];
629
+ return Array.isArray(value) ? value[0] : value;
543
630
  };
631
+ return inBandChallengeResult(failure, this.resolveResourceMetadataUrl?.(header));
544
632
  }
545
633
  }
546
634
  const result = await cb(args, extra);
@@ -607,16 +695,12 @@ export class McpServer extends McpServerBaseOmitted {
607
695
  }
608
696
  registerTool(...args) {
609
697
  const baseFn = McpServerBase.prototype.registerTool;
610
- if (typeof args[0] === "string") {
611
- baseFn.call(this, args[0], args[1], args[2]);
612
- return this;
613
- }
614
- const config = args[0];
615
- const cb = args[1];
698
+ const { config, cb } = normalizeRegisterToolArgs(args);
616
699
  const { name, view, auth, securitySchemes: rawSecuritySchemes, _meta: userToolMeta, ...toolFields } = config;
700
+ const authNeedsProvider = auth !== undefined &&
701
+ (!auth.allowsAnonymous || Boolean(auth.scopes?.length));
617
702
  if (rawSecuritySchemes === undefined &&
618
- auth !== undefined &&
619
- auth !== "public" &&
703
+ authNeedsProvider &&
620
704
  !this.oauthEnabled) {
621
705
  throw new Error(`Tool "${name}" sets \`auth: ${JSON.stringify(auth)}\` but the server has no \`oauth\` provider configured.`);
622
706
  }