skybridge 0.0.0-dev.da7ba17 → 0.0.0-dev.dafb590

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 (130) hide show
  1. package/dist/server/auth/discovery.js +1 -13
  2. package/dist/server/auth/discovery.js.map +1 -1
  3. package/dist/server/auth/discovery.test.js +0 -22
  4. package/dist/server/auth/discovery.test.js.map +1 -1
  5. package/dist/server/auth/providers/custom.d.ts +4 -16
  6. package/dist/server/auth/providers/custom.js +9 -18
  7. package/dist/server/auth/providers/custom.js.map +1 -1
  8. package/dist/server/auth/providers/custom.test.js +4 -20
  9. package/dist/server/auth/providers/custom.test.js.map +1 -1
  10. package/dist/server/auth/setup.js +2 -2
  11. package/dist/server/auth/setup.js.map +1 -1
  12. package/dist/server/auth/verify.d.ts +2 -3
  13. package/dist/server/auth/verify.js +1 -2
  14. package/dist/server/auth/verify.js.map +1 -1
  15. package/dist/server/auth/verify.test.js +0 -13
  16. package/dist/server/auth/verify.test.js.map +1 -1
  17. package/dist/server/index.d.ts +0 -5
  18. package/dist/server/index.js +0 -5
  19. package/dist/server/index.js.map +1 -1
  20. package/dist/server/server.d.ts +4 -12
  21. package/dist/server/server.js +99 -68
  22. package/dist/server/server.js.map +1 -1
  23. package/dist/server/view-resource-resolution.test.js +9 -24
  24. package/dist/server/view-resource-resolution.test.js.map +1 -1
  25. package/dist/test/view.test.js +101 -69
  26. package/dist/test/view.test.js.map +1 -1
  27. package/dist/web/bridges/apps-sdk/adaptor.d.ts +29 -0
  28. package/dist/web/bridges/apps-sdk/adaptor.js +117 -0
  29. package/dist/web/bridges/apps-sdk/adaptor.js.map +1 -0
  30. package/dist/web/bridges/apps-sdk/bridge.d.ts +2 -6
  31. package/dist/web/bridges/apps-sdk/bridge.js +4 -15
  32. package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
  33. package/dist/web/bridges/apps-sdk/index.d.ts +1 -0
  34. package/dist/web/bridges/apps-sdk/index.js +1 -0
  35. package/dist/web/bridges/apps-sdk/index.js.map +1 -1
  36. package/dist/web/bridges/get-adaptor.d.ts +4 -4
  37. package/dist/web/bridges/get-adaptor.js +11 -6
  38. package/dist/web/bridges/get-adaptor.js.map +1 -1
  39. package/dist/web/bridges/mcp-app/adaptor.d.ts +53 -0
  40. package/dist/web/bridges/mcp-app/adaptor.js +283 -0
  41. package/dist/web/bridges/mcp-app/adaptor.js.map +1 -0
  42. package/dist/web/bridges/mcp-app/bridge.d.ts +2 -13
  43. package/dist/web/bridges/mcp-app/bridge.js +4 -46
  44. package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
  45. package/dist/web/bridges/mcp-app/index.d.ts +1 -0
  46. package/dist/web/bridges/mcp-app/index.js +1 -0
  47. package/dist/web/bridges/mcp-app/index.js.map +1 -1
  48. package/dist/web/bridges/mcp-app/view-tools.test.js +6 -5
  49. package/dist/web/bridges/mcp-app/view-tools.test.js.map +1 -1
  50. package/dist/web/bridges/types.d.ts +0 -10
  51. package/dist/web/bridges/types.js +1 -14
  52. package/dist/web/bridges/types.js.map +1 -1
  53. package/dist/web/components/modal-provider.js +3 -4
  54. package/dist/web/components/modal-provider.js.map +1 -1
  55. package/dist/web/create-store.test.js +5 -8
  56. package/dist/web/create-store.test.js.map +1 -1
  57. package/dist/web/data-llm.test.js +5 -9
  58. package/dist/web/data-llm.test.js.map +1 -1
  59. package/dist/web/hooks/use-call-tool.test.js +23 -37
  60. package/dist/web/hooks/use-call-tool.test.js.map +1 -1
  61. package/dist/web/hooks/use-display-mode.test.js +20 -56
  62. package/dist/web/hooks/use-display-mode.test.js.map +1 -1
  63. package/dist/web/hooks/use-download.test.js +3 -11
  64. package/dist/web/hooks/use-download.test.js.map +1 -1
  65. package/dist/web/hooks/use-files.test.js +2 -10
  66. package/dist/web/hooks/use-files.test.js.map +1 -1
  67. package/dist/web/hooks/use-layout.test.js +26 -59
  68. package/dist/web/hooks/use-layout.test.js.map +1 -1
  69. package/dist/web/hooks/use-open-external.test.js +3 -13
  70. package/dist/web/hooks/use-open-external.test.js.map +1 -1
  71. package/dist/web/hooks/use-request-close.test.js +40 -35
  72. package/dist/web/hooks/use-request-close.test.js.map +1 -1
  73. package/dist/web/hooks/use-request-modal.test.js +0 -10
  74. package/dist/web/hooks/use-request-modal.test.js.map +1 -1
  75. package/dist/web/hooks/use-request-size.test.js +53 -35
  76. package/dist/web/hooks/use-request-size.test.js.map +1 -1
  77. package/dist/web/hooks/use-set-open-in-app-url.test.js +8 -28
  78. package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -1
  79. package/dist/web/hooks/use-tool-info.test.js +27 -37
  80. package/dist/web/hooks/use-tool-info.test.js.map +1 -1
  81. package/dist/web/hooks/use-user.test.js +35 -81
  82. package/dist/web/hooks/use-user.test.js.map +1 -1
  83. package/dist/web/hooks/use-view-state.test.js +2 -6
  84. package/dist/web/hooks/use-view-state.test.js.map +1 -1
  85. package/package.json +1 -1
  86. package/dist/server/auth/providers/auth0.d.ts +0 -18
  87. package/dist/server/auth/providers/auth0.js +0 -31
  88. package/dist/server/auth/providers/auth0.js.map +0 -1
  89. package/dist/server/auth/providers/auth0.test.d.ts +0 -1
  90. package/dist/server/auth/providers/auth0.test.js +0 -48
  91. package/dist/server/auth/providers/auth0.test.js.map +0 -1
  92. package/dist/server/auth/providers/clerk.d.ts +0 -14
  93. package/dist/server/auth/providers/clerk.js +0 -16
  94. package/dist/server/auth/providers/clerk.js.map +0 -1
  95. package/dist/server/auth/providers/clerk.test.d.ts +0 -1
  96. package/dist/server/auth/providers/clerk.test.js +0 -28
  97. package/dist/server/auth/providers/clerk.test.js.map +0 -1
  98. package/dist/server/auth/providers/descope.d.ts +0 -15
  99. package/dist/server/auth/providers/descope.js +0 -33
  100. package/dist/server/auth/providers/descope.js.map +0 -1
  101. package/dist/server/auth/providers/descope.test.d.ts +0 -1
  102. package/dist/server/auth/providers/descope.test.js +0 -37
  103. package/dist/server/auth/providers/descope.test.js.map +0 -1
  104. package/dist/server/auth/providers/shared.d.ts +0 -2
  105. package/dist/server/auth/providers/shared.js +0 -6
  106. package/dist/server/auth/providers/shared.js.map +0 -1
  107. package/dist/server/auth/providers/shared.test.d.ts +0 -1
  108. package/dist/server/auth/providers/shared.test.js +0 -10
  109. package/dist/server/auth/providers/shared.test.js.map +0 -1
  110. package/dist/server/auth/providers/stytch.d.ts +0 -12
  111. package/dist/server/auth/providers/stytch.js +0 -13
  112. package/dist/server/auth/providers/stytch.js.map +0 -1
  113. package/dist/server/auth/providers/workos.d.ts +0 -11
  114. package/dist/server/auth/providers/workos.js +0 -12
  115. package/dist/server/auth/providers/workos.js.map +0 -1
  116. package/dist/web/bridges/adaptor.d.ts +0 -51
  117. package/dist/web/bridges/adaptor.js +0 -310
  118. package/dist/web/bridges/adaptor.js.map +0 -1
  119. package/dist/web/bridges/adaptor.test.d.ts +0 -1
  120. package/dist/web/bridges/adaptor.test.js +0 -197
  121. package/dist/web/bridges/adaptor.test.js.map +0 -1
  122. package/dist/web/bridges/get-adaptor.test.d.ts +0 -1
  123. package/dist/web/bridges/get-adaptor.test.js +0 -32
  124. package/dist/web/bridges/get-adaptor.test.js.map +0 -1
  125. package/dist/web/bridges/mcp-app/bridge.test.d.ts +0 -1
  126. package/dist/web/bridges/mcp-app/bridge.test.js +0 -17
  127. package/dist/web/bridges/mcp-app/bridge.test.js.map +0 -1
  128. package/dist/web/bridges/types.test.d.ts +0 -1
  129. package/dist/web/bridges/types.test.js +0 -19
  130. package/dist/web/bridges/types.test.js.map +0 -1
@@ -17,9 +17,6 @@ const DISCOVERY_TIMEOUT_MS = 5000;
17
17
  export async function discoverAuthorizationServer(issuer) {
18
18
  const base = issuer.replace(/\/$/, "");
19
19
  const errors = [];
20
- // First valid doc lacking a registration_endpoint, kept as a fallback so a
21
- // genuinely non-DCR IdP still resolves (and is rejected later by the caller).
22
- let fallback;
23
20
  for (const path of WELL_KNOWN) {
24
21
  const url = `${base}${path}`;
25
22
  // Any failure (network/timeout, non-2xx, non-JSON, invalid metadata, issuer
@@ -36,21 +33,12 @@ export async function discoverAuthorizationServer(issuer) {
36
33
  if (meta.issuer.replace(/\/$/, "") !== base) {
37
34
  throw new Error(`issuer mismatch: ${meta.issuer}`);
38
35
  }
39
- // Prefer the document that advertises DCR: some IdPs (e.g. Clerk) serve a
40
- // valid openid-configuration without `registration_endpoint` yet expose
41
- // it at oauth-authorization-server. Keep looking past a doc that omits it.
42
- if (meta.registration_endpoint) {
43
- return meta;
44
- }
45
- fallback ??= meta;
36
+ return meta;
46
37
  }
47
38
  catch (err) {
48
39
  errors.push(`${url}: ${err instanceof Error ? err.message : String(err)}`);
49
40
  }
50
41
  }
51
- if (fallback) {
52
- return fallback;
53
- }
54
42
  throw new Error(`OAuth discovery failed for ${issuer}: ${errors.join("; ")}`);
55
43
  }
56
44
  //# sourceMappingURL=discovery.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../../src/server/auth/discovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AAC/E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;GAIG;AACH,MAAM,eAAe,GAAG,mBAAmB,CAAC,MAAM,CAAC;IACjD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAChC,CAAC,CAAC;AAGH,MAAM,UAAU,GAAG;IACjB,mCAAmC;IACnC,yCAAyC;CAC1C,CAAC;AAEF,MAAM,oBAAoB,GAAG,IAAI,CAAC;AAElC,0EAA0E;AAC1E,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,MAAc;IAEd,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACvC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,2EAA2E;IAC3E,8EAA8E;IAC9E,IAAI,QAAwC,CAAC;IAE7C,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC;QAC7B,4EAA4E;QAC5E,yEAAyE;QACzE,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAC3B,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,oBAAoB,CAAC;aAClD,CAAC,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;YACxC,CAAC;YACD,MAAM,IAAI,GAAG,eAAe,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;YACrD,yEAAyE;YACzE,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;gBAC5C,MAAM,IAAI,KAAK,CAAC,oBAAoB,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YACrD,CAAC;YACD,0EAA0E;YAC1E,wEAAwE;YACxE,2EAA2E;YAC3E,IAAI,IAAI,CAAC,qBAAqB,EAAE,CAAC;gBAC/B,OAAO,IAAI,CAAC;YACd,CAAC;YACD,QAAQ,KAAK,IAAI,CAAC;QACpB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,CACT,GAAG,GAAG,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAC9D,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,8BAA8B,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAChF,CAAC","sourcesContent":["import { OAuthMetadataSchema } from \"@modelcontextprotocol/sdk/shared/auth.js\";\nimport { z } from \"zod\";\n\n/**\n * Discovery doc validated as OAuth AS metadata + `jwks_uri`. The SDK's\n * `OAuthMetadataSchema` omits `jwks_uri` (its client never verifies tokens), but\n * we need it for JWKS token verification, so we extend the schema to keep it.\n */\nconst DiscoverySchema = OAuthMetadataSchema.extend({\n jwks_uri: z.string().optional(),\n});\nexport type DiscoveredMetadata = z.infer<typeof DiscoverySchema>;\n\nconst WELL_KNOWN = [\n \"/.well-known/openid-configuration\",\n \"/.well-known/oauth-authorization-server\",\n];\n\nconst DISCOVERY_TIMEOUT_MS = 5000;\n\n/** Fetches and validates an authorization server's discovery document. */\nexport async function discoverAuthorizationServer(\n issuer: string,\n): Promise<DiscoveredMetadata> {\n const base = issuer.replace(/\\/$/, \"\");\n const errors: string[] = [];\n // First valid doc lacking a registration_endpoint, kept as a fallback so a\n // genuinely non-DCR IdP still resolves (and is rejected later by the caller).\n let fallback: DiscoveredMetadata | undefined;\n\n for (const path of WELL_KNOWN) {\n const url = `${base}${path}`;\n // Any failure (network/timeout, non-2xx, non-JSON, invalid metadata, issuer\n // mismatch) records a per-URL reason and falls through to the next path.\n try {\n const res = await fetch(url, {\n signal: AbortSignal.timeout(DISCOVERY_TIMEOUT_MS),\n });\n if (!res.ok) {\n throw new Error(`HTTP ${res.status}`);\n }\n const meta = DiscoverySchema.parse(await res.json());\n // RFC 8414 §3.3: issuer must match the fetch origin (slash-insensitive).\n if (meta.issuer.replace(/\\/$/, \"\") !== base) {\n throw new Error(`issuer mismatch: ${meta.issuer}`);\n }\n // Prefer the document that advertises DCR: some IdPs (e.g. Clerk) serve a\n // valid openid-configuration without `registration_endpoint` yet expose\n // it at oauth-authorization-server. Keep looking past a doc that omits it.\n if (meta.registration_endpoint) {\n return meta;\n }\n fallback ??= meta;\n } catch (err) {\n errors.push(\n `${url}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n if (fallback) {\n return fallback;\n }\n throw new Error(`OAuth discovery failed for ${issuer}: ${errors.join(\"; \")}`);\n}\n"]}
1
+ {"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../../src/server/auth/discovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AAC/E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;GAIG;AACH,MAAM,eAAe,GAAG,mBAAmB,CAAC,MAAM,CAAC;IACjD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAChC,CAAC,CAAC;AAGH,MAAM,UAAU,GAAG;IACjB,mCAAmC;IACnC,yCAAyC;CAC1C,CAAC;AAEF,MAAM,oBAAoB,GAAG,IAAI,CAAC;AAElC,0EAA0E;AAC1E,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,MAAc;IAEd,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACvC,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC;QAC7B,4EAA4E;QAC5E,yEAAyE;QACzE,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAC3B,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,oBAAoB,CAAC;aAClD,CAAC,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;YACxC,CAAC;YACD,MAAM,IAAI,GAAG,eAAe,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;YACrD,yEAAyE;YACzE,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;gBAC5C,MAAM,IAAI,KAAK,CAAC,oBAAoB,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YACrD,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,CACT,GAAG,GAAG,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAC9D,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,8BAA8B,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAChF,CAAC","sourcesContent":["import { OAuthMetadataSchema } from \"@modelcontextprotocol/sdk/shared/auth.js\";\nimport { z } from \"zod\";\n\n/**\n * Discovery doc validated as OAuth AS metadata + `jwks_uri`. The SDK's\n * `OAuthMetadataSchema` omits `jwks_uri` (its client never verifies tokens), but\n * we need it for JWKS token verification, so we extend the schema to keep it.\n */\nconst DiscoverySchema = OAuthMetadataSchema.extend({\n jwks_uri: z.string().optional(),\n});\nexport type DiscoveredMetadata = z.infer<typeof DiscoverySchema>;\n\nconst WELL_KNOWN = [\n \"/.well-known/openid-configuration\",\n \"/.well-known/oauth-authorization-server\",\n];\n\nconst DISCOVERY_TIMEOUT_MS = 5000;\n\n/** Fetches and validates an authorization server's discovery document. */\nexport async function discoverAuthorizationServer(\n issuer: string,\n): Promise<DiscoveredMetadata> {\n const base = issuer.replace(/\\/$/, \"\");\n const errors: string[] = [];\n\n for (const path of WELL_KNOWN) {\n const url = `${base}${path}`;\n // Any failure (network/timeout, non-2xx, non-JSON, invalid metadata, issuer\n // mismatch) records a per-URL reason and falls through to the next path.\n try {\n const res = await fetch(url, {\n signal: AbortSignal.timeout(DISCOVERY_TIMEOUT_MS),\n });\n if (!res.ok) {\n throw new Error(`HTTP ${res.status}`);\n }\n const meta = DiscoverySchema.parse(await res.json());\n // RFC 8414 §3.3: issuer must match the fetch origin (slash-insensitive).\n if (meta.issuer.replace(/\\/$/, \"\") !== base) {\n throw new Error(`issuer mismatch: ${meta.issuer}`);\n }\n return meta;\n } catch (err) {\n errors.push(\n `${url}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n throw new Error(`OAuth discovery failed for ${issuer}: ${errors.join(\"; \")}`);\n}\n"]}
@@ -61,28 +61,6 @@ describe("discoverAuthorizationServer", () => {
61
61
  const base = await serve(() => ({}));
62
62
  await expect(discoverAuthorizationServer(base)).rejects.toThrow(/discovery failed/i);
63
63
  });
64
- it("falls through to oauth-authorization-server when oidc doc is schema-invalid", async () => {
65
- const base = await serve((o) => ({
66
- "/.well-known/openid-configuration": doc(o, {
67
- response_types_supported: undefined,
68
- }),
69
- "/.well-known/oauth-authorization-server": doc(o),
70
- }));
71
- const meta = await discoverAuthorizationServer(base);
72
- expect(meta.registration_endpoint).toBe(`${base}/register`);
73
- });
74
- it("prefers the doc advertising DCR when oidc omits registration_endpoint", async () => {
75
- // Clerk-shaped: valid openid-configuration without a registration_endpoint,
76
- // DCR exposed only at oauth-authorization-server.
77
- const base = await serve((o) => ({
78
- "/.well-known/openid-configuration": doc(o, {
79
- registration_endpoint: undefined,
80
- }),
81
- "/.well-known/oauth-authorization-server": doc(o),
82
- }));
83
- const meta = await discoverAuthorizationServer(base);
84
- expect(meta.registration_endpoint).toBe(`${base}/register`);
85
- });
86
64
  it("rejects a doc whose issuer does not match the fetch origin", async () => {
87
65
  const base = await serve(() => ({
88
66
  "/.well-known/openid-configuration": doc("https://evil.test"),
@@ -1 +1 @@
1
- {"version":3,"file":"discovery.test.js","sourceRoot":"","sources":["../../../src/server/auth/discovery.test.ts"],"names":[],"mappings":"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","sourcesContent":["// @vitest-environment node\nimport http from \"node:http\";\nimport { afterEach, describe, expect, it } from \"vitest\";\nimport { discoverAuthorizationServer } from \"./discovery.js\";\n\nlet server: http.Server | undefined;\nafterEach(() => server?.close());\n\nfunction doc(origin: string, extra: Record<string, unknown> = {}) {\n return {\n issuer: origin,\n authorization_endpoint: `${origin}/authorize`,\n token_endpoint: `${origin}/token`,\n registration_endpoint: `${origin}/register`,\n response_types_supported: [\"code\"],\n scopes_supported: [\"openid\", \"email\"],\n jwks_uri: `${origin}/jwks`,\n ...extra,\n };\n}\n\n// Serves JSON bodies built from the live origin at their well-known paths;\n// returns the origin URL.\nasync function serve(build: (origin: string) => Record<string, unknown>) {\n let origin = \"\";\n const srv = http.createServer((req, res) => {\n const body = build(origin)[req.url ?? \"\"];\n if (body === undefined) {\n res.writeHead(404).end();\n return;\n }\n res.setHeader(\"content-type\", \"application/json\");\n res.end(JSON.stringify(body));\n });\n await new Promise<void>((resolve) => srv.listen(0, resolve));\n server = srv;\n origin = `http://localhost:${(srv.address() as { port: number }).port}`;\n return origin;\n}\n\ndescribe(\"discoverAuthorizationServer\", () => {\n it(\"fetches and validates openid-configuration\", async () => {\n const base = await serve((o) => ({\n \"/.well-known/openid-configuration\": doc(o),\n }));\n const meta = await discoverAuthorizationServer(base);\n expect(meta.registration_endpoint).toBe(`${base}/register`);\n expect(meta.jwks_uri).toBe(`${base}/jwks`);\n });\n\n it(\"accepts a canonically slash-terminated issuer\", async () => {\n const base = await serve((o) => ({\n \"/.well-known/openid-configuration\": doc(o, { issuer: `${o}/` }),\n }));\n const meta = await discoverAuthorizationServer(base);\n expect(meta.registration_endpoint).toBe(`${base}/register`);\n });\n\n it(\"falls back to oauth-authorization-server when oidc is 404\", async () => {\n const base = await serve((o) => ({\n \"/.well-known/oauth-authorization-server\": doc(o),\n }));\n const meta = await discoverAuthorizationServer(base);\n expect(meta.registration_endpoint).toBe(`${base}/register`);\n });\n\n it(\"throws when no well-known doc is reachable\", async () => {\n const base = await serve(() => ({}));\n await expect(discoverAuthorizationServer(base)).rejects.toThrow(\n /discovery failed/i,\n );\n });\n\n it(\"falls through to oauth-authorization-server when oidc doc is schema-invalid\", async () => {\n const base = await serve((o) => ({\n \"/.well-known/openid-configuration\": doc(o, {\n response_types_supported: undefined,\n }),\n \"/.well-known/oauth-authorization-server\": doc(o),\n }));\n const meta = await discoverAuthorizationServer(base);\n expect(meta.registration_endpoint).toBe(`${base}/register`);\n });\n\n it(\"prefers the doc advertising DCR when oidc omits registration_endpoint\", async () => {\n // Clerk-shaped: valid openid-configuration without a registration_endpoint,\n // DCR exposed only at oauth-authorization-server.\n const base = await serve((o) => ({\n \"/.well-known/openid-configuration\": doc(o, {\n registration_endpoint: undefined,\n }),\n \"/.well-known/oauth-authorization-server\": doc(o),\n }));\n const meta = await discoverAuthorizationServer(base);\n expect(meta.registration_endpoint).toBe(`${base}/register`);\n });\n\n it(\"rejects a doc whose issuer does not match the fetch origin\", async () => {\n const base = await serve(() => ({\n \"/.well-known/openid-configuration\": doc(\"https://evil.test\"),\n }));\n await expect(discoverAuthorizationServer(base)).rejects.toThrow(\n /discovery failed/i,\n );\n });\n});\n"]}
1
+ {"version":3,"file":"discovery.test.js","sourceRoot":"","sources":["../../../src/server/auth/discovery.test.ts"],"names":[],"mappings":"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","sourcesContent":["// @vitest-environment node\nimport http from \"node:http\";\nimport { afterEach, describe, expect, it } from \"vitest\";\nimport { discoverAuthorizationServer } from \"./discovery.js\";\n\nlet server: http.Server | undefined;\nafterEach(() => server?.close());\n\nfunction doc(origin: string, extra: Record<string, unknown> = {}) {\n return {\n issuer: origin,\n authorization_endpoint: `${origin}/authorize`,\n token_endpoint: `${origin}/token`,\n registration_endpoint: `${origin}/register`,\n response_types_supported: [\"code\"],\n scopes_supported: [\"openid\", \"email\"],\n jwks_uri: `${origin}/jwks`,\n ...extra,\n };\n}\n\n// Serves JSON bodies built from the live origin at their well-known paths;\n// returns the origin URL.\nasync function serve(build: (origin: string) => Record<string, unknown>) {\n let origin = \"\";\n const srv = http.createServer((req, res) => {\n const body = build(origin)[req.url ?? \"\"];\n if (body === undefined) {\n res.writeHead(404).end();\n return;\n }\n res.setHeader(\"content-type\", \"application/json\");\n res.end(JSON.stringify(body));\n });\n await new Promise<void>((resolve) => srv.listen(0, resolve));\n server = srv;\n origin = `http://localhost:${(srv.address() as { port: number }).port}`;\n return origin;\n}\n\ndescribe(\"discoverAuthorizationServer\", () => {\n it(\"fetches and validates openid-configuration\", async () => {\n const base = await serve((o) => ({\n \"/.well-known/openid-configuration\": doc(o),\n }));\n const meta = await discoverAuthorizationServer(base);\n expect(meta.registration_endpoint).toBe(`${base}/register`);\n expect(meta.jwks_uri).toBe(`${base}/jwks`);\n });\n\n it(\"accepts a canonically slash-terminated issuer\", async () => {\n const base = await serve((o) => ({\n \"/.well-known/openid-configuration\": doc(o, { issuer: `${o}/` }),\n }));\n const meta = await discoverAuthorizationServer(base);\n expect(meta.registration_endpoint).toBe(`${base}/register`);\n });\n\n it(\"falls back to oauth-authorization-server when oidc is 404\", async () => {\n const base = await serve((o) => ({\n \"/.well-known/oauth-authorization-server\": doc(o),\n }));\n const meta = await discoverAuthorizationServer(base);\n expect(meta.registration_endpoint).toBe(`${base}/register`);\n });\n\n it(\"throws when no well-known doc is reachable\", async () => {\n const base = await serve(() => ({}));\n await expect(discoverAuthorizationServer(base)).rejects.toThrow(\n /discovery failed/i,\n );\n });\n\n it(\"rejects a doc whose issuer does not match the fetch origin\", async () => {\n const base = await serve(() => ({\n \"/.well-known/openid-configuration\": doc(\"https://evil.test\"),\n }));\n await expect(discoverAuthorizationServer(base)).rejects.toThrow(\n /discovery failed/i,\n );\n });\n});\n"]}
@@ -1,24 +1,12 @@
1
1
  import type { OAuthMetadata } from "@modelcontextprotocol/sdk/shared/auth.js";
2
2
  import type { OAuthConfig } from "../index.js";
3
- /** Options accepted by {@link customProvider} and the branded providers. */
4
- export type CustomProviderOptions = {
3
+ /** Builds a complete {@link OAuthConfig} for a DCR-compatible IdP via discovery. */
4
+ export declare function customProvider(opts: {
5
5
  issuer: string;
6
- /** Expected token `aud`. Omit to skip audience verification — only for IdPs
7
- * that don't bind an audience (e.g. Clerk). Branded providers whose IdP does
8
- * bind an audience re-require it in their own options. */
9
- audience?: string;
6
+ audience: string;
10
7
  /** Omit to let the server infer the resource origin from request headers. */
11
8
  baseUrl?: string;
12
- /** Advertise THIS server as the authorization server (skybridge-as-AS): the
13
- * served AS metadata `issuer` and the PRM `authorization_servers` use this URL
14
- * instead of the IdP's. Needed when skybridge must sit in the auth path — Auth0
15
- * (the `audience` must be baked into `/authorize`) or the Alpic DCR proxy (Alpic
16
- * injects the registration endpoint). Use the static public URL; `verify.issuer`
17
- * stays the IdP (the token's real `iss`). */
18
- serverUrl?: string;
19
9
  scopes?: string[];
20
10
  requiredScopes?: string[];
21
11
  metadataOverrides?: Omit<Partial<OAuthMetadata>, "issuer">;
22
- };
23
- /** Builds a complete {@link OAuthConfig} from an IdP's OAuth discovery document. */
24
- export declare function customProvider(opts: CustomProviderOptions): Promise<OAuthConfig>;
12
+ }): Promise<OAuthConfig>;
@@ -1,27 +1,18 @@
1
1
  import { discoverAuthorizationServer, } from "../discovery.js";
2
- /** Builds a complete {@link OAuthConfig} from an IdP's OAuth discovery document. */
2
+ /** Builds a complete {@link OAuthConfig} for a DCR-compatible IdP via discovery. */
3
3
  export async function customProvider(opts) {
4
4
  const discovered = await discoverAuthorizationServer(opts.issuer);
5
- // JWKS verification needs a signing-key URL; without it the server can't verify
6
- // tokens. (`registration_endpoint` is optional per RFC 8414 — a no-DCR IdP simply
7
- // omits it; a proxy like Alpic, or pre-registered clients, supply registration.)
5
+ // The IdP's own discovery must advertise DCR and a JWKS; overrides can't fake it.
6
+ if (!discovered.registration_endpoint) {
7
+ throw new Error(`${opts.issuer} is not DCR-compatible (no registration_endpoint); customProvider only supports DCR IdPs.`);
8
+ }
8
9
  if (!discovered.jwks_uri) {
9
10
  throw new Error(`${opts.issuer} discovery has no jwks_uri; JWKS verification requires it.`);
10
11
  }
11
- // Overrides adjust only advertised metadata; the trust anchor (issuer, jwks_uri)
12
- // always comes from validated discovery.
12
+ // Overrides adjust only advertised metadata (e.g. proxied endpoints). The
13
+ // trust anchor (issuer, jwks_uri) always comes from validated discovery.
13
14
  const { issuer: _issuer, jwks_uri: _jwks, ...overrides } = opts.metadataOverrides ?? {};
14
- const base = { ...discovered, ...overrides };
15
- const scopesSupported = opts.scopes ?? base.scopes_supported;
16
- // serverUrl => skybridge-as-AS: advertise this server's URL as the issuer (and
17
- // keep the served scopes in sync). The verifier still trusts the IdP's `iss`.
18
- const oauthMetadata = opts.serverUrl
19
- ? {
20
- ...base,
21
- issuer: opts.serverUrl.replace(/\/$/, ""),
22
- scopes_supported: scopesSupported,
23
- }
24
- : base;
15
+ const oauthMetadata = { ...discovered, ...overrides };
25
16
  return {
26
17
  baseUrl: opts.baseUrl,
27
18
  oauthMetadata,
@@ -30,7 +21,7 @@ export async function customProvider(opts) {
30
21
  audience: opts.audience,
31
22
  jwksUri: discovered.jwks_uri,
32
23
  },
33
- scopesSupported,
24
+ scopesSupported: opts.scopes ?? oauthMetadata.scopes_supported,
34
25
  requiredScopes: opts.requiredScopes,
35
26
  };
36
27
  }
@@ -1 +1 @@
1
- {"version":3,"file":"custom.js","sourceRoot":"","sources":["../../../../src/server/auth/providers/custom.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,2BAA2B,GAC5B,MAAM,iBAAiB,CAAC;AAwBzB,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAA2B;IAE3B,MAAM,UAAU,GAAG,MAAM,2BAA2B,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAElE,gFAAgF;IAChF,kFAAkF;IAClF,iFAAiF;IACjF,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,CAAC,MAAM,4DAA4D,CAC3E,CAAC;IACJ,CAAC;IAED,iFAAiF;IACjF,yCAAyC;IACzC,MAAM,EACJ,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,KAAK,EACf,GAAG,SAAS,EACb,GAAgC,IAAI,CAAC,iBAAiB,IAAI,EAAE,CAAC;IAC9D,MAAM,IAAI,GAAuB,EAAE,GAAG,UAAU,EAAE,GAAG,SAAS,EAAE,CAAC;IACjE,MAAM,eAAe,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,gBAAgB,CAAC;IAE7D,+EAA+E;IAC/E,8EAA8E;IAC9E,MAAM,aAAa,GAAuB,IAAI,CAAC,SAAS;QACtD,CAAC,CAAC;YACE,GAAG,IAAI;YACP,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;YACzC,gBAAgB,EAAE,eAAe;SAClC;QACH,CAAC,CAAC,IAAI,CAAC;IAET,OAAO;QACL,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,aAAa;QACb,MAAM,EAAE;YACN,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,OAAO,EAAE,UAAU,CAAC,QAAQ;SAC7B;QACD,eAAe;QACf,cAAc,EAAE,IAAI,CAAC,cAAc;KACpC,CAAC;AACJ,CAAC","sourcesContent":["import type { OAuthMetadata } from \"@modelcontextprotocol/sdk/shared/auth.js\";\nimport {\n type DiscoveredMetadata,\n discoverAuthorizationServer,\n} from \"../discovery.js\";\nimport type { OAuthConfig } from \"../index.js\";\n\n/** Options accepted by {@link customProvider} and the branded providers. */\nexport type CustomProviderOptions = {\n issuer: string;\n /** Expected token `aud`. Omit to skip audience verification — only for IdPs\n * that don't bind an audience (e.g. Clerk). Branded providers whose IdP does\n * bind an audience re-require it in their own options. */\n audience?: string;\n /** Omit to let the server infer the resource origin from request headers. */\n baseUrl?: string;\n /** Advertise THIS server as the authorization server (skybridge-as-AS): the\n * served AS metadata `issuer` and the PRM `authorization_servers` use this URL\n * instead of the IdP's. Needed when skybridge must sit in the auth path — Auth0\n * (the `audience` must be baked into `/authorize`) or the Alpic DCR proxy (Alpic\n * injects the registration endpoint). Use the static public URL; `verify.issuer`\n * stays the IdP (the token's real `iss`). */\n serverUrl?: string;\n scopes?: string[];\n requiredScopes?: string[];\n metadataOverrides?: Omit<Partial<OAuthMetadata>, \"issuer\">;\n};\n\n/** Builds a complete {@link OAuthConfig} from an IdP's OAuth discovery document. */\nexport async function customProvider(\n opts: CustomProviderOptions,\n): Promise<OAuthConfig> {\n const discovered = await discoverAuthorizationServer(opts.issuer);\n\n // JWKS verification needs a signing-key URL; without it the server can't verify\n // tokens. (`registration_endpoint` is optional per RFC 8414 a no-DCR IdP simply\n // omits it; a proxy like Alpic, or pre-registered clients, supply registration.)\n if (!discovered.jwks_uri) {\n throw new Error(\n `${opts.issuer} discovery has no jwks_uri; JWKS verification requires it.`,\n );\n }\n\n // Overrides adjust only advertised metadata; the trust anchor (issuer, jwks_uri)\n // always comes from validated discovery.\n const {\n issuer: _issuer,\n jwks_uri: _jwks,\n ...overrides\n }: Partial<DiscoveredMetadata> = opts.metadataOverrides ?? {};\n const base: DiscoveredMetadata = { ...discovered, ...overrides };\n const scopesSupported = opts.scopes ?? base.scopes_supported;\n\n // serverUrl => skybridge-as-AS: advertise this server's URL as the issuer (and\n // keep the served scopes in sync). The verifier still trusts the IdP's `iss`.\n const oauthMetadata: DiscoveredMetadata = opts.serverUrl\n ? {\n ...base,\n issuer: opts.serverUrl.replace(/\\/$/, \"\"),\n scopes_supported: scopesSupported,\n }\n : base;\n\n return {\n baseUrl: opts.baseUrl,\n oauthMetadata,\n verify: {\n issuer: discovered.issuer,\n audience: opts.audience,\n jwksUri: discovered.jwks_uri,\n },\n scopesSupported,\n requiredScopes: opts.requiredScopes,\n };\n}\n"]}
1
+ {"version":3,"file":"custom.js","sourceRoot":"","sources":["../../../../src/server/auth/providers/custom.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,2BAA2B,GAC5B,MAAM,iBAAiB,CAAC;AAGzB,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAQpC;IACC,MAAM,UAAU,GAAG,MAAM,2BAA2B,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAElE,kFAAkF;IAClF,IAAI,CAAC,UAAU,CAAC,qBAAqB,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,CAAC,MAAM,2FAA2F,CAC1G,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,CAAC,MAAM,4DAA4D,CAC3E,CAAC;IACJ,CAAC;IAED,0EAA0E;IAC1E,yEAAyE;IACzE,MAAM,EACJ,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,KAAK,EACf,GAAG,SAAS,EACb,GAAgC,IAAI,CAAC,iBAAiB,IAAI,EAAE,CAAC;IAC9D,MAAM,aAAa,GAAuB,EAAE,GAAG,UAAU,EAAE,GAAG,SAAS,EAAE,CAAC;IAE1E,OAAO;QACL,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,aAAa;QACb,MAAM,EAAE;YACN,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,OAAO,EAAE,UAAU,CAAC,QAAQ;SAC7B;QACD,eAAe,EAAE,IAAI,CAAC,MAAM,IAAI,aAAa,CAAC,gBAAgB;QAC9D,cAAc,EAAE,IAAI,CAAC,cAAc;KACpC,CAAC;AACJ,CAAC","sourcesContent":["import type { OAuthMetadata } from \"@modelcontextprotocol/sdk/shared/auth.js\";\nimport {\n type DiscoveredMetadata,\n discoverAuthorizationServer,\n} from \"../discovery.js\";\nimport type { OAuthConfig } from \"../index.js\";\n\n/** Builds a complete {@link OAuthConfig} for a DCR-compatible IdP via discovery. */\nexport async function customProvider(opts: {\n issuer: string;\n audience: string;\n /** Omit to let the server infer the resource origin from request headers. */\n baseUrl?: string;\n scopes?: string[];\n requiredScopes?: string[];\n metadataOverrides?: Omit<Partial<OAuthMetadata>, \"issuer\">;\n}): Promise<OAuthConfig> {\n const discovered = await discoverAuthorizationServer(opts.issuer);\n\n // The IdP's own discovery must advertise DCR and a JWKS; overrides can't fake it.\n if (!discovered.registration_endpoint) {\n throw new Error(\n `${opts.issuer} is not DCR-compatible (no registration_endpoint); customProvider only supports DCR IdPs.`,\n );\n }\n if (!discovered.jwks_uri) {\n throw new Error(\n `${opts.issuer} discovery has no jwks_uri; JWKS verification requires it.`,\n );\n }\n\n // Overrides adjust only advertised metadata (e.g. proxied endpoints). The\n // trust anchor (issuer, jwks_uri) always comes from validated discovery.\n const {\n issuer: _issuer,\n jwks_uri: _jwks,\n ...overrides\n }: Partial<DiscoveredMetadata> = opts.metadataOverrides ?? {};\n const oauthMetadata: DiscoveredMetadata = { ...discovered, ...overrides };\n\n return {\n baseUrl: opts.baseUrl,\n oauthMetadata,\n verify: {\n issuer: discovered.issuer,\n audience: opts.audience,\n jwksUri: discovered.jwks_uri,\n },\n scopesSupported: opts.scopes ?? oauthMetadata.scopes_supported,\n requiredScopes: opts.requiredScopes,\n };\n}\n"]}
@@ -69,29 +69,13 @@ describe("customProvider", () => {
69
69
  });
70
70
  expect(config.verify.jwksUri).toBe(`${base}/jwks`);
71
71
  });
72
- it("serves metadata without a registration_endpoint when the IdP has no DCR", async () => {
72
+ it("rejects a non-DCR IdP even if an override supplies registration_endpoint", async () => {
73
73
  const base = await serveDiscovery({ registration_endpoint: undefined });
74
- const config = await customProvider({ issuer: base, audience: "a" });
75
- expect(config.oauthMetadata.registration_endpoint).toBeUndefined();
76
- expect(config.verify.issuer).toBe(base);
77
- });
78
- it("throws when discovery has no jwks_uri (token verification needs it)", async () => {
79
- const base = await serveDiscovery({ jwks_uri: undefined });
80
- await expect(customProvider({ issuer: base, audience: "a" })).rejects.toThrow(/jwks_uri/);
81
- });
82
- it("serverUrl advertises this server as the AS, keeping the IdP as the token issuer", async () => {
83
- const base = await serveDiscovery();
84
- const config = await customProvider({
74
+ await expect(customProvider({
85
75
  issuer: base,
86
76
  audience: "a",
87
- serverUrl: "https://app.example.test/",
88
- scopes: ["openid"],
89
- });
90
- // AS metadata + advertised scopes reflect this server.
91
- expect(config.oauthMetadata.issuer).toBe("https://app.example.test");
92
- expect(config.oauthMetadata.scopes_supported).toEqual(["openid"]);
93
- // but the token's issuer is still verified against the IdP.
94
- expect(config.verify.issuer).toBe(base);
77
+ metadataOverrides: { registration_endpoint: `${base}/register` },
78
+ })).rejects.toThrow(/not DCR-compatible/);
95
79
  });
96
80
  it("applies metadataOverrides over discovered values", async () => {
97
81
  const base = await serveDiscovery();
@@ -1 +1 @@
1
- {"version":3,"file":"custom.test.js","sourceRoot":"","sources":["../../../../src/server/auth/providers/custom.test.ts"],"names":[],"mappings":"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","sourcesContent":["// @vitest-environment node\nimport http from \"node:http\";\nimport { afterEach, describe, expect, it } from \"vitest\";\nimport { customProvider } from \"./custom.js\";\n\nlet server: http.Server | undefined;\nafterEach(() => server?.close());\n\nfunction doc(origin: string, extra: Record<string, unknown> = {}) {\n return {\n issuer: origin,\n authorization_endpoint: `${origin}/authorize`,\n token_endpoint: `${origin}/token`,\n registration_endpoint: `${origin}/register`,\n response_types_supported: [\"code\"],\n scopes_supported: [\"openid\", \"email\", \"profile\"],\n jwks_uri: `${origin}/jwks`,\n ...extra,\n };\n}\n\n// Serves the discovery doc (built from the live origin) at the OIDC well-known path.\nasync function serveDiscovery(extra: Record<string, unknown> = {}) {\n let origin = \"\";\n const srv = http.createServer((req, res) => {\n if (req.url !== \"/.well-known/openid-configuration\") {\n res.writeHead(404).end();\n return;\n }\n res.setHeader(\"content-type\", \"application/json\");\n res.end(JSON.stringify(doc(origin, extra)));\n });\n await new Promise<void>((resolve) => srv.listen(0, resolve));\n server = srv;\n origin = `http://localhost:${(srv.address() as { port: number }).port}`;\n return origin;\n}\n\ndescribe(\"customProvider\", () => {\n it(\"builds an OAuthConfig from discovery\", async () => {\n const base = await serveDiscovery();\n\n const config = await customProvider({\n issuer: base,\n audience: \"my-api\",\n baseUrl: \"https://app.example.test\",\n scopes: [\"openid\"],\n });\n\n expect(config.baseUrl).toBe(\"https://app.example.test\");\n expect(config.verify).toEqual({\n issuer: base,\n audience: \"my-api\",\n jwksUri: `${base}/jwks`,\n });\n expect(config.scopesSupported).toEqual([\"openid\"]);\n expect(config.oauthMetadata.registration_endpoint).toBe(`${base}/register`);\n });\n\n it(\"ignores a runtime issuer override (keeps the discovered trust anchor)\", async () => {\n const base = await serveDiscovery();\n const config = await customProvider({\n issuer: base,\n audience: \"a\",\n metadataOverrides: { issuer: \"https://evil.test\" } as never,\n });\n expect(config.verify.issuer).toBe(base);\n expect(config.oauthMetadata.issuer).toBe(base);\n });\n\n it(\"ignores a runtime jwks_uri override (keeps the discovered signing keys)\", async () => {\n const base = await serveDiscovery();\n const config = await customProvider({\n issuer: base,\n audience: \"a\",\n metadataOverrides: { jwks_uri: \"https://evil.test/keys\" } as never,\n });\n expect(config.verify.jwksUri).toBe(`${base}/jwks`);\n });\n\n it(\"serves metadata without a registration_endpoint when the IdP has no DCR\", async () => {\n const base = await serveDiscovery({ registration_endpoint: undefined });\n const config = await customProvider({ issuer: base, audience: \"a\" });\n expect(config.oauthMetadata.registration_endpoint).toBeUndefined();\n expect(config.verify.issuer).toBe(base);\n });\n\n it(\"throws when discovery has no jwks_uri (token verification needs it)\", async () => {\n const base = await serveDiscovery({ jwks_uri: undefined });\n await expect(\n customProvider({ issuer: base, audience: \"a\" }),\n ).rejects.toThrow(/jwks_uri/);\n });\n\n it(\"serverUrl advertises this server as the AS, keeping the IdP as the token issuer\", async () => {\n const base = await serveDiscovery();\n const config = await customProvider({\n issuer: base,\n audience: \"a\",\n serverUrl: \"https://app.example.test/\",\n scopes: [\"openid\"],\n });\n // AS metadata + advertised scopes reflect this server.\n expect(config.oauthMetadata.issuer).toBe(\"https://app.example.test\");\n expect(config.oauthMetadata.scopes_supported).toEqual([\"openid\"]);\n // but the token's issuer is still verified against the IdP.\n expect(config.verify.issuer).toBe(base);\n });\n\n it(\"applies metadataOverrides over discovered values\", async () => {\n const base = await serveDiscovery();\n const config = await customProvider({\n issuer: base,\n audience: \"a\",\n baseUrl: \"https://app.example.test\",\n metadataOverrides: { token_endpoint: \"https://override/token\" },\n });\n expect(config.oauthMetadata.token_endpoint).toBe(\"https://override/token\");\n });\n});\n"]}
1
+ {"version":3,"file":"custom.test.js","sourceRoot":"","sources":["../../../../src/server/auth/providers/custom.test.ts"],"names":[],"mappings":"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","sourcesContent":["// @vitest-environment node\nimport http from \"node:http\";\nimport { afterEach, describe, expect, it } from \"vitest\";\nimport { customProvider } from \"./custom.js\";\n\nlet server: http.Server | undefined;\nafterEach(() => server?.close());\n\nfunction doc(origin: string, extra: Record<string, unknown> = {}) {\n return {\n issuer: origin,\n authorization_endpoint: `${origin}/authorize`,\n token_endpoint: `${origin}/token`,\n registration_endpoint: `${origin}/register`,\n response_types_supported: [\"code\"],\n scopes_supported: [\"openid\", \"email\", \"profile\"],\n jwks_uri: `${origin}/jwks`,\n ...extra,\n };\n}\n\n// Serves the discovery doc (built from the live origin) at the OIDC well-known path.\nasync function serveDiscovery(extra: Record<string, unknown> = {}) {\n let origin = \"\";\n const srv = http.createServer((req, res) => {\n if (req.url !== \"/.well-known/openid-configuration\") {\n res.writeHead(404).end();\n return;\n }\n res.setHeader(\"content-type\", \"application/json\");\n res.end(JSON.stringify(doc(origin, extra)));\n });\n await new Promise<void>((resolve) => srv.listen(0, resolve));\n server = srv;\n origin = `http://localhost:${(srv.address() as { port: number }).port}`;\n return origin;\n}\n\ndescribe(\"customProvider\", () => {\n it(\"builds an OAuthConfig from discovery\", async () => {\n const base = await serveDiscovery();\n\n const config = await customProvider({\n issuer: base,\n audience: \"my-api\",\n baseUrl: \"https://app.example.test\",\n scopes: [\"openid\"],\n });\n\n expect(config.baseUrl).toBe(\"https://app.example.test\");\n expect(config.verify).toEqual({\n issuer: base,\n audience: \"my-api\",\n jwksUri: `${base}/jwks`,\n });\n expect(config.scopesSupported).toEqual([\"openid\"]);\n expect(config.oauthMetadata.registration_endpoint).toBe(`${base}/register`);\n });\n\n it(\"ignores a runtime issuer override (keeps the discovered trust anchor)\", async () => {\n const base = await serveDiscovery();\n const config = await customProvider({\n issuer: base,\n audience: \"a\",\n metadataOverrides: { issuer: \"https://evil.test\" } as never,\n });\n expect(config.verify.issuer).toBe(base);\n expect(config.oauthMetadata.issuer).toBe(base);\n });\n\n it(\"ignores a runtime jwks_uri override (keeps the discovered signing keys)\", async () => {\n const base = await serveDiscovery();\n const config = await customProvider({\n issuer: base,\n audience: \"a\",\n metadataOverrides: { jwks_uri: \"https://evil.test/keys\" } as never,\n });\n expect(config.verify.jwksUri).toBe(`${base}/jwks`);\n });\n\n it(\"rejects a non-DCR IdP even if an override supplies registration_endpoint\", async () => {\n const base = await serveDiscovery({ registration_endpoint: undefined });\n await expect(\n customProvider({\n issuer: base,\n audience: \"a\",\n metadataOverrides: { registration_endpoint: `${base}/register` },\n }),\n ).rejects.toThrow(/not DCR-compatible/);\n });\n\n it(\"applies metadataOverrides over discovered values\", async () => {\n const base = await serveDiscovery();\n const config = await customProvider({\n issuer: base,\n audience: \"a\",\n baseUrl: \"https://app.example.test\",\n metadataOverrides: { token_endpoint: \"https://override/token\" },\n });\n expect(config.oauthMetadata.token_endpoint).toBe(\"https://override/token\");\n });\n});\n"]}
@@ -5,8 +5,8 @@ import { resolveServerOrigin } from "../requestOrigin.js";
5
5
  import { createJwksVerifier } from "./verify.js";
6
6
  /** Mounts the well-known OAuth metadata and bearer auth on `/mcp`. */
7
7
  export function setupOAuth(app, config) {
8
- if (!config.verify?.issuer) {
9
- throw new Error("oauth.verify requires an `issuer`");
8
+ if (!config.verify?.issuer || !config.verify?.audience) {
9
+ throw new Error("oauth.verify requires both `issuer` and `audience`");
10
10
  }
11
11
  const verifier = createJwksVerifier(config.verify);
12
12
  // baseUrl known at boot: bake the resource URLs once, no Host-header trust.
@@ -1 +1 @@
1
- {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../../src/server/auth/setup.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oCAAoC,EACpC,qBAAqB,GACtB,MAAM,iDAAiD,CAAC;AACzD,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,sEAAsE;AACtE,MAAM,UAAU,UAAU,CAAC,GAAY,EAAE,MAAmB;IAC1D,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,QAAQ,GAAG,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAEnD,4EAA4E;IAC5E,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACjC,IAAI,OAAY,CAAC;QACjB,IAAI,CAAC;YACH,OAAO,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CACb,oDAAoD,IAAI,CAAC,SAAS,CAChE,MAAM,CAAC,OAAO,CACf,EAAE,CACJ,CAAC;QACJ,CAAC;QACD,GAAG,CAAC,GAAG,CACL,qBAAqB,CAAC;YACpB,aAAa,EAAE,MAAM,CAAC,aAAa;YACnC,iBAAiB,EAAE,OAAO;YAC1B,eAAe,EAAE,MAAM,CAAC,eAAe;SACxC,CAAC,CACH,CAAC;QACF,GAAG,CAAC,GAAG,CACL,MAAM,EACN,iBAAiB,CAAC;YAChB,QAAQ;YACR,cAAc,EAAE,MAAM,CAAC,cAAc;YACrC,mBAAmB,EAAE,oCAAoC,CAAC,OAAO,CAAC;SACnE,CAAC,CACH,CAAC;QACF,OAAO;IACT,CAAC;IAED,6EAA6E;IAC7E,8EAA8E;IAC9E,6EAA6E;IAC7E,MAAM,aAAa,GAAG,CAAC,GAAY,EAAE,EAAE,CACrC,IAAI,GAAG,CAAC,mBAAmB,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAEtD,GAAG,CAAC,GAAG,CACL,yCAAyC,EACzC,IAAI,EAAE,EACN,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CACnD,CAAC;IACF,GAAG,CAAC,GAAG,CAAC,uCAAuC,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACpE,GAAG,CAAC,IAAI,CAAC;YACP,QAAQ,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI;YACjC,qBAAqB,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC;YACpD,gBAAgB,EAAE,MAAM,CAAC,eAAe;SACzC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,CACjC,iBAAiB,CAAC;QAChB,QAAQ;QACR,cAAc,EAAE,MAAM,CAAC,cAAc;QACrC,mBAAmB,EAAE,oCAAoC,CACvD,aAAa,CAAC,GAAG,CAAC,CACnB;KACF,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CACnB,CAAC;AACJ,CAAC","sourcesContent":["import {\n getOAuthProtectedResourceMetadataUrl,\n mcpAuthMetadataRouter,\n} from \"@modelcontextprotocol/sdk/server/auth/router.js\";\nimport cors from \"cors\";\nimport type { Express, Request } from \"express\";\nimport { requireBearerAuth } from \"../auth.js\";\nimport { resolveServerOrigin } from \"../requestOrigin.js\";\nimport type { OAuthConfig } from \"./index.js\";\nimport { createJwksVerifier } from \"./verify.js\";\n\n/** Mounts the well-known OAuth metadata and bearer auth on `/mcp`. */\nexport function setupOAuth(app: Express, config: OAuthConfig): void {\n if (!config.verify?.issuer) {\n throw new Error(\"oauth.verify requires an `issuer`\");\n }\n\n const verifier = createJwksVerifier(config.verify);\n\n // baseUrl known at boot: bake the resource URLs once, no Host-header trust.\n if (config.baseUrl !== undefined) {\n let baseUrl: URL;\n try {\n baseUrl = new URL(config.baseUrl);\n } catch {\n throw new Error(\n `oauth.baseUrl must be a valid absolute URL, got: ${JSON.stringify(\n config.baseUrl,\n )}`,\n );\n }\n app.use(\n mcpAuthMetadataRouter({\n oauthMetadata: config.oauthMetadata,\n resourceServerUrl: baseUrl,\n scopesSupported: config.scopesSupported,\n }),\n );\n app.use(\n \"/mcp\",\n requireBearerAuth({\n verifier,\n requiredScopes: config.requiredScopes,\n resourceMetadataUrl: getOAuthProtectedResourceMetadataUrl(baseUrl),\n }),\n );\n return;\n }\n\n // No baseUrl: resolve the resource origin per request from forwarded headers\n // (same precedence the framework uses for view serverUrl). Origin headers are\n // pathless, so the PRM path stays at root, matching the SDK's static layout.\n const resolveOrigin = (req: Request) =>\n new URL(resolveServerOrigin((key) => req.get(key)));\n\n app.use(\n \"/.well-known/oauth-authorization-server\",\n cors(),\n (_req, res) => void res.json(config.oauthMetadata),\n );\n app.use(\"/.well-known/oauth-protected-resource\", cors(), (req, res) => {\n res.json({\n resource: resolveOrigin(req).href,\n authorization_servers: [config.oauthMetadata.issuer],\n scopes_supported: config.scopesSupported,\n });\n });\n app.use(\"/mcp\", (req, res, next) =>\n requireBearerAuth({\n verifier,\n requiredScopes: config.requiredScopes,\n resourceMetadataUrl: getOAuthProtectedResourceMetadataUrl(\n resolveOrigin(req),\n ),\n })(req, res, next),\n );\n}\n"]}
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../../src/server/auth/setup.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oCAAoC,EACpC,qBAAqB,GACtB,MAAM,iDAAiD,CAAC;AACzD,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,sEAAsE;AACtE,MAAM,UAAU,UAAU,CAAC,GAAY,EAAE,MAAmB;IAC1D,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACxE,CAAC;IAED,MAAM,QAAQ,GAAG,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAEnD,4EAA4E;IAC5E,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACjC,IAAI,OAAY,CAAC;QACjB,IAAI,CAAC;YACH,OAAO,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CACb,oDAAoD,IAAI,CAAC,SAAS,CAChE,MAAM,CAAC,OAAO,CACf,EAAE,CACJ,CAAC;QACJ,CAAC;QACD,GAAG,CAAC,GAAG,CACL,qBAAqB,CAAC;YACpB,aAAa,EAAE,MAAM,CAAC,aAAa;YACnC,iBAAiB,EAAE,OAAO;YAC1B,eAAe,EAAE,MAAM,CAAC,eAAe;SACxC,CAAC,CACH,CAAC;QACF,GAAG,CAAC,GAAG,CACL,MAAM,EACN,iBAAiB,CAAC;YAChB,QAAQ;YACR,cAAc,EAAE,MAAM,CAAC,cAAc;YACrC,mBAAmB,EAAE,oCAAoC,CAAC,OAAO,CAAC;SACnE,CAAC,CACH,CAAC;QACF,OAAO;IACT,CAAC;IAED,6EAA6E;IAC7E,8EAA8E;IAC9E,6EAA6E;IAC7E,MAAM,aAAa,GAAG,CAAC,GAAY,EAAE,EAAE,CACrC,IAAI,GAAG,CAAC,mBAAmB,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAEtD,GAAG,CAAC,GAAG,CACL,yCAAyC,EACzC,IAAI,EAAE,EACN,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CACnD,CAAC;IACF,GAAG,CAAC,GAAG,CAAC,uCAAuC,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACpE,GAAG,CAAC,IAAI,CAAC;YACP,QAAQ,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI;YACjC,qBAAqB,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC;YACpD,gBAAgB,EAAE,MAAM,CAAC,eAAe;SACzC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,CACjC,iBAAiB,CAAC;QAChB,QAAQ;QACR,cAAc,EAAE,MAAM,CAAC,cAAc;QACrC,mBAAmB,EAAE,oCAAoC,CACvD,aAAa,CAAC,GAAG,CAAC,CACnB;KACF,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CACnB,CAAC;AACJ,CAAC","sourcesContent":["import {\n getOAuthProtectedResourceMetadataUrl,\n mcpAuthMetadataRouter,\n} from \"@modelcontextprotocol/sdk/server/auth/router.js\";\nimport cors from \"cors\";\nimport type { Express, Request } from \"express\";\nimport { requireBearerAuth } from \"../auth.js\";\nimport { resolveServerOrigin } from \"../requestOrigin.js\";\nimport type { OAuthConfig } from \"./index.js\";\nimport { createJwksVerifier } from \"./verify.js\";\n\n/** Mounts the well-known OAuth metadata and bearer auth on `/mcp`. */\nexport function setupOAuth(app: Express, config: OAuthConfig): void {\n if (!config.verify?.issuer || !config.verify?.audience) {\n throw new Error(\"oauth.verify requires both `issuer` and `audience`\");\n }\n\n const verifier = createJwksVerifier(config.verify);\n\n // baseUrl known at boot: bake the resource URLs once, no Host-header trust.\n if (config.baseUrl !== undefined) {\n let baseUrl: URL;\n try {\n baseUrl = new URL(config.baseUrl);\n } catch {\n throw new Error(\n `oauth.baseUrl must be a valid absolute URL, got: ${JSON.stringify(\n config.baseUrl,\n )}`,\n );\n }\n app.use(\n mcpAuthMetadataRouter({\n oauthMetadata: config.oauthMetadata,\n resourceServerUrl: baseUrl,\n scopesSupported: config.scopesSupported,\n }),\n );\n app.use(\n \"/mcp\",\n requireBearerAuth({\n verifier,\n requiredScopes: config.requiredScopes,\n resourceMetadataUrl: getOAuthProtectedResourceMetadataUrl(baseUrl),\n }),\n );\n return;\n }\n\n // No baseUrl: resolve the resource origin per request from forwarded headers\n // (same precedence the framework uses for view serverUrl). Origin headers are\n // pathless, so the PRM path stays at root, matching the SDK's static layout.\n const resolveOrigin = (req: Request) =>\n new URL(resolveServerOrigin((key) => req.get(key)));\n\n app.use(\n \"/.well-known/oauth-authorization-server\",\n cors(),\n (_req, res) => void res.json(config.oauthMetadata),\n );\n app.use(\"/.well-known/oauth-protected-resource\", cors(), (req, res) => {\n res.json({\n resource: resolveOrigin(req).href,\n authorization_servers: [config.oauthMetadata.issuer],\n scopes_supported: config.scopesSupported,\n });\n });\n app.use(\"/mcp\", (req, res, next) =>\n requireBearerAuth({\n verifier,\n requiredScopes: config.requiredScopes,\n resourceMetadataUrl: getOAuthProtectedResourceMetadataUrl(\n resolveOrigin(req),\n ),\n })(req, res, next),\n );\n}\n"]}
@@ -2,9 +2,8 @@ import type { OAuthTokenVerifier } from "@modelcontextprotocol/sdk/server/auth/p
2
2
  export type JwksVerifyConfig = {
3
3
  /** Expected `iss` claim. */
4
4
  issuer: string;
5
- /** Expected `aud` claim. Omit to skip audience verification — for IdPs that
6
- * don't bind an audience to their access tokens (e.g. Clerk). */
7
- audience?: string;
5
+ /** Expected `aud` claim. */
6
+ audience: string;
8
7
  /** Defaults to `${issuer}/.well-known/jwks.json`. */
9
8
  jwksUri?: string;
10
9
  };
@@ -11,8 +11,7 @@ export function createJwksVerifier(config) {
11
11
  try {
12
12
  ({ payload } = await jose.jwtVerify(token, jwks, {
13
13
  issuer: config.issuer,
14
- // jose skips the aud check when `audience` is undefined.
15
- ...(config.audience !== undefined && { audience: config.audience }),
14
+ audience: config.audience,
16
15
  }));
17
16
  }
18
17
  catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"verify.js","sourceRoot":"","sources":["../../../src/server/auth/verify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iDAAiD,CAAC;AAGpF,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAY7B,oGAAoG;AACpG,MAAM,UAAU,kBAAkB,CAChC,MAAwB;IAExB,MAAM,OAAO,GACX,MAAM,CAAC,OAAO;QACd,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,wBAAwB,CAAC;IAC9D,MAAM,IAAI,GAAG,IAAI,CAAC,kBAAkB,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;IAEvD,OAAO;QACL,KAAK,CAAC,iBAAiB,CAAC,KAAa;YACnC,IAAI,OAAwB,CAAC;YAC7B,IAAI,CAAC;gBACH,CAAC,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE;oBAC/C,MAAM,EAAE,MAAM,CAAC,MAAM;oBACrB,yDAAyD;oBACzD,GAAG,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;iBACpE,CAAC,CAAC,CAAC;YACN,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACjE,MAAM,IAAI,iBAAiB,CAAC,8BAA8B,OAAO,EAAE,CAAC,CAAC;YACvE,CAAC;YAED,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,OAQ/C,CAAC;YAEF,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;gBACjC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC;gBACnB,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ;oBACzB,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;oBACpC,CAAC,CAAC,EAAE,CAAC;YAET,OAAO;gBACL,KAAK;gBACL,QAAQ,EAAE,SAAS,IAAI,EAAE;gBACzB,MAAM;gBACN,SAAS,EAAE,GAAG;gBACd,KAAK,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE;aACd,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { InvalidTokenError } from \"@modelcontextprotocol/sdk/server/auth/errors.js\";\nimport type { OAuthTokenVerifier } from \"@modelcontextprotocol/sdk/server/auth/provider.js\";\nimport type { AuthInfo } from \"@modelcontextprotocol/sdk/server/auth/types.js\";\nimport * as jose from \"jose\";\n\nexport type JwksVerifyConfig = {\n /** Expected `iss` claim. */\n issuer: string;\n /** Expected `aud` claim. Omit to skip audience verification — for IdPs that\n * don't bind an audience to their access tokens (e.g. Clerk). */\n audience?: string;\n /** Defaults to `${issuer}/.well-known/jwks.json`. */\n jwksUri?: string;\n};\n\n/** Builds an `OAuthTokenVerifier` validating JWTs against a remote JWKS. Internal, not exported. */\nexport function createJwksVerifier(\n config: JwksVerifyConfig,\n): OAuthTokenVerifier {\n const jwksUri =\n config.jwksUri ??\n `${config.issuer.replace(/\\/$/, \"\")}/.well-known/jwks.json`;\n const jwks = jose.createRemoteJWKSet(new URL(jwksUri));\n\n return {\n async verifyAccessToken(token: string): Promise<AuthInfo> {\n let payload: jose.JWTPayload;\n try {\n ({ payload } = await jose.jwtVerify(token, jwks, {\n issuer: config.issuer,\n // jose skips the aud check when `audience` is undefined.\n ...(config.audience !== undefined && { audience: config.audience }),\n }));\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new InvalidTokenError(`Token verification failed: ${message}`);\n }\n\n const { client_id, scope, exp, sub, ...rest } = payload as Record<\n string,\n unknown\n > & {\n client_id?: string;\n scope?: string | string[];\n exp?: number;\n sub?: string;\n };\n\n const scopes = Array.isArray(scope)\n ? scope.map(String)\n : typeof scope === \"string\"\n ? scope.split(/\\s+/).filter(Boolean)\n : [];\n\n return {\n token,\n clientId: client_id ?? \"\",\n scopes,\n expiresAt: exp,\n extra: { subject: sub, ...rest },\n } satisfies AuthInfo;\n },\n };\n}\n"]}
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../../../src/server/auth/verify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iDAAiD,CAAC;AAGpF,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAW7B,oGAAoG;AACpG,MAAM,UAAU,kBAAkB,CAChC,MAAwB;IAExB,MAAM,OAAO,GACX,MAAM,CAAC,OAAO;QACd,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,wBAAwB,CAAC;IAC9D,MAAM,IAAI,GAAG,IAAI,CAAC,kBAAkB,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;IAEvD,OAAO;QACL,KAAK,CAAC,iBAAiB,CAAC,KAAa;YACnC,IAAI,OAAwB,CAAC;YAC7B,IAAI,CAAC;gBACH,CAAC,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE;oBAC/C,MAAM,EAAE,MAAM,CAAC,MAAM;oBACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;iBAC1B,CAAC,CAAC,CAAC;YACN,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACjE,MAAM,IAAI,iBAAiB,CAAC,8BAA8B,OAAO,EAAE,CAAC,CAAC;YACvE,CAAC;YAED,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,OAQ/C,CAAC;YAEF,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;gBACjC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC;gBACnB,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ;oBACzB,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;oBACpC,CAAC,CAAC,EAAE,CAAC;YAET,OAAO;gBACL,KAAK;gBACL,QAAQ,EAAE,SAAS,IAAI,EAAE;gBACzB,MAAM;gBACN,SAAS,EAAE,GAAG;gBACd,KAAK,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE;aACd,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { InvalidTokenError } from \"@modelcontextprotocol/sdk/server/auth/errors.js\";\nimport type { OAuthTokenVerifier } from \"@modelcontextprotocol/sdk/server/auth/provider.js\";\nimport type { AuthInfo } from \"@modelcontextprotocol/sdk/server/auth/types.js\";\nimport * as jose from \"jose\";\n\nexport type JwksVerifyConfig = {\n /** Expected `iss` claim. */\n issuer: string;\n /** Expected `aud` claim. */\n audience: string;\n /** Defaults to `${issuer}/.well-known/jwks.json`. */\n jwksUri?: string;\n};\n\n/** Builds an `OAuthTokenVerifier` validating JWTs against a remote JWKS. Internal, not exported. */\nexport function createJwksVerifier(\n config: JwksVerifyConfig,\n): OAuthTokenVerifier {\n const jwksUri =\n config.jwksUri ??\n `${config.issuer.replace(/\\/$/, \"\")}/.well-known/jwks.json`;\n const jwks = jose.createRemoteJWKSet(new URL(jwksUri));\n\n return {\n async verifyAccessToken(token: string): Promise<AuthInfo> {\n let payload: jose.JWTPayload;\n try {\n ({ payload } = await jose.jwtVerify(token, jwks, {\n issuer: config.issuer,\n audience: config.audience,\n }));\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new InvalidTokenError(`Token verification failed: ${message}`);\n }\n\n const { client_id, scope, exp, sub, ...rest } = payload as Record<\n string,\n unknown\n > & {\n client_id?: string;\n scope?: string | string[];\n exp?: number;\n sub?: string;\n };\n\n const scopes = Array.isArray(scope)\n ? scope.map(String)\n : typeof scope === \"string\"\n ? scope.split(/\\s+/).filter(Boolean)\n : [];\n\n return {\n token,\n clientId: client_id ?? \"\",\n scopes,\n expiresAt: exp,\n extra: { subject: sub, ...rest },\n } satisfies AuthInfo;\n },\n };\n}\n"]}
@@ -63,19 +63,6 @@ describe("createJwksVerifier", () => {
63
63
  const token = await sign(privateKey, { client_id: "c" }, { audience: "api://other" });
64
64
  await expect(verifier.verifyAccessToken(token)).rejects.toThrow(/Token verification failed/);
65
65
  });
66
- it("skips the aud check when no audience is configured (e.g. Clerk)", async () => {
67
- const { privateKey, jwksUri } = await startJwks();
68
- const verifier = createJwksVerifier({ issuer: ISSUER, jwksUri });
69
- // Clerk-shaped access token: no `aud` claim at all.
70
- const token = await new jose.SignJWT({ client_id: "c", sub: "u" })
71
- .setProtectedHeader({ alg: "RS256", kid: "test-key" })
72
- .setIssuer(ISSUER)
73
- .setExpirationTime("1h")
74
- .sign(privateKey);
75
- const auth = await verifier.verifyAccessToken(token);
76
- expect(auth.clientId).toBe("c");
77
- expect(auth.extra?.subject).toBe("u");
78
- });
79
66
  it("parses array scope claims and trims extra whitespace", async () => {
80
67
  const { privateKey, jwksUri } = await startJwks();
81
68
  const verifier = createJwksVerifier({
@@ -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(\"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(\"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,10 +1,5 @@
1
1
  export type { OAuthConfig } from "./auth/index.js";
2
- export { auth0Provider } from "./auth/providers/auth0.js";
3
- export { clerkProvider } from "./auth/providers/clerk.js";
4
2
  export { customProvider } from "./auth/providers/custom.js";
5
- export { descopeProvider } from "./auth/providers/descope.js";
6
- export { stytchProvider } from "./auth/providers/stytch.js";
7
- export { workosProvider } from "./auth/providers/workos.js";
8
3
  export { type AuthInfo, type AuthMetadataOptions, type BearerAuthMiddlewareOptions, InvalidTokenError, mcpAuthMetadataRouter, optionalBearerAuth, requireBearerAuth, } from "./auth.js";
9
4
  export { audio, embeddedResource, image, resourceLink, text, } from "./content-helpers.js";
10
5
  export { FileRef } from "./file-ref.js";
@@ -1,9 +1,4 @@
1
- export { auth0Provider } from "./auth/providers/auth0.js";
2
- export { clerkProvider } from "./auth/providers/clerk.js";
3
1
  export { customProvider } from "./auth/providers/custom.js";
4
- export { descopeProvider } from "./auth/providers/descope.js";
5
- export { stytchProvider } from "./auth/providers/stytch.js";
6
- export { workosProvider } from "./auth/providers/workos.js";
7
2
  export { InvalidTokenError, mcpAuthMetadataRouter, optionalBearerAuth, requireBearerAuth, } from "./auth.js";
8
3
  export { audio, embeddedResource, image, resourceLink, text, } from "./content-helpers.js";
9
4
  export { FileRef } from "./file-ref.js";
@@ -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,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 { customProvider } from \"./auth/providers/custom.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"]}
@@ -19,11 +19,7 @@ export type ToolDef<TInput = unknown, TOutput = unknown, TResponseMetadata = unk
19
19
  output: TOutput;
20
20
  responseMetadata: TResponseMetadata;
21
21
  };
22
- /**
23
- * @deprecated Views now always emit a single ext-apps resource; host targeting
24
- * no longer applies. Retained for backwards compatibility; will be removed in a
25
- * future major.
26
- */
22
+ /** Which host runtime a view targets — `"apps-sdk"` (ChatGPT) or `"mcp-app"` (MCP Apps spec). */
27
23
  export type ViewHostType = "apps-sdk" | "mcp-app";
28
24
  /**
29
25
  * Content Security Policy origins attached to a view's resource. Each list is
@@ -60,14 +56,11 @@ export interface ViewConfig {
60
56
  component: ViewName;
61
57
  /** Human-readable label the host may show alongside the view. */
62
58
  description?: string;
63
- /**
64
- * @deprecated No-op. Every view emits a single ext-apps resource regardless
65
- * of this value. Will be removed in a future major.
66
- */
59
+ /** Restrict where the view is rendered. Defaults to all known hosts. */
67
60
  hosts?: ViewHostType[];
68
- /** Request a visible border around the view (forwarded as `ui.prefersBorder`). */
61
+ /** Apps SDK only: request a visible border around the widget. */
69
62
  prefersBorder?: boolean;
70
- /** Override the iframe's served domain (advanced; forwarded as `ui.domain`). */
63
+ /** Apps SDK only: override the iframe's served domain (advanced). */
71
64
  domain?: string;
72
65
  /** Per-view CSP overrides — see {@link ViewCsp}. */
73
66
  csp?: ViewCsp;
@@ -357,7 +350,6 @@ export declare class McpServer<TTools extends Record<string, ToolDef> = Record<n
357
350
  private resolveViewRequestContext;
358
351
  private registerViewResources;
359
352
  private registerViewResource;
360
- private serveLegacyAppsSdkUrl;
361
353
  private wrapHandler;
362
354
  private computeViewVersionParam;
363
355
  private lookupViewFile;