skybridge 0.0.0-dev.81dc563 → 0.0.0-dev.81fd90a

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 (316) hide show
  1. package/README.md +3 -0
  2. package/dist/cli/build-helpers.d.ts +6 -2
  3. package/dist/cli/build-helpers.js +37 -12
  4. package/dist/cli/build-helpers.js.map +1 -1
  5. package/dist/cli/build-helpers.test.js +71 -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.js +52 -0
  11. package/dist/cli/build-steps.test.js.map +1 -0
  12. package/dist/cli/header.d.ts +1 -1
  13. package/dist/cli/resolve-skybridge-config.d.ts +5 -0
  14. package/dist/cli/{resolve-views-dir.js → resolve-skybridge-config.js} +4 -3
  15. package/dist/cli/resolve-skybridge-config.js.map +1 -0
  16. package/dist/cli/run-plain.d.ts +18 -0
  17. package/dist/cli/run-plain.js +89 -0
  18. package/dist/cli/run-plain.js.map +1 -0
  19. package/dist/cli/use-execute-steps.d.ts +1 -1
  20. package/dist/cli/use-nodemon.d.ts +14 -0
  21. package/dist/cli/use-nodemon.js +71 -60
  22. package/dist/cli/use-nodemon.js.map +1 -1
  23. package/dist/cli/use-typescript-check.d.ts +8 -2
  24. package/dist/cli/use-typescript-check.js +70 -67
  25. package/dist/cli/use-typescript-check.js.map +1 -1
  26. package/dist/commands/build.d.ts +0 -2
  27. package/dist/commands/build.js +2 -61
  28. package/dist/commands/build.js.map +1 -1
  29. package/dist/commands/dev.d.ts +1 -0
  30. package/dist/commands/dev.js +34 -2
  31. package/dist/commands/dev.js.map +1 -1
  32. package/dist/context-warnings.d.ts +5 -0
  33. package/dist/context-warnings.js +31 -0
  34. package/dist/context-warnings.js.map +1 -0
  35. package/dist/context-warnings.test.d.ts +1 -0
  36. package/dist/context-warnings.test.js +28 -0
  37. package/dist/context-warnings.test.js.map +1 -0
  38. package/dist/server/auth/discovery.d.ts +33 -0
  39. package/dist/server/auth/discovery.js +56 -0
  40. package/dist/server/auth/discovery.js.map +1 -0
  41. package/dist/server/auth/discovery.test.d.ts +1 -0
  42. package/dist/server/auth/discovery.test.js +93 -0
  43. package/dist/server/auth/discovery.test.js.map +1 -0
  44. package/dist/server/auth/index.d.ts +17 -7
  45. package/dist/server/auth/index.js.map +1 -1
  46. package/dist/server/auth/providers/auth0.d.ts +40 -0
  47. package/dist/server/auth/providers/auth0.js +31 -0
  48. package/dist/server/auth/providers/auth0.js.map +1 -0
  49. package/dist/server/auth/providers/auth0.test.d.ts +1 -0
  50. package/dist/server/auth/providers/auth0.test.js +50 -0
  51. package/dist/server/auth/providers/auth0.test.js.map +1 -0
  52. package/dist/server/auth/providers/authplane.d.ts +49 -0
  53. package/dist/server/auth/providers/authplane.js +57 -0
  54. package/dist/server/auth/providers/authplane.js.map +1 -0
  55. package/dist/server/auth/providers/authplane.test.d.ts +1 -0
  56. package/dist/server/auth/providers/authplane.test.js +140 -0
  57. package/dist/server/auth/providers/authplane.test.js.map +1 -0
  58. package/dist/server/auth/providers/clerk.d.ts +51 -0
  59. package/dist/server/auth/providers/clerk.js +19 -0
  60. package/dist/server/auth/providers/clerk.js.map +1 -0
  61. package/dist/server/auth/providers/clerk.test.d.ts +1 -0
  62. package/dist/server/auth/providers/clerk.test.js +30 -0
  63. package/dist/server/auth/providers/clerk.test.js.map +1 -0
  64. package/dist/server/auth/providers/custom.d.ts +40 -0
  65. package/dist/server/auth/providers/custom.js +56 -0
  66. package/dist/server/auth/providers/custom.js.map +1 -0
  67. package/dist/server/auth/providers/custom.test.d.ts +1 -0
  68. package/dist/server/auth/providers/custom.test.js +158 -0
  69. package/dist/server/auth/providers/custom.test.js.map +1 -0
  70. package/dist/server/auth/providers/descope.d.ts +44 -0
  71. package/dist/server/auth/providers/descope.js +44 -0
  72. package/dist/server/auth/providers/descope.js.map +1 -0
  73. package/dist/server/auth/providers/descope.test.d.ts +1 -0
  74. package/dist/server/auth/providers/descope.test.js +62 -0
  75. package/dist/server/auth/providers/descope.test.js.map +1 -0
  76. package/dist/server/auth/providers/shared.d.ts +2 -0
  77. package/dist/server/auth/providers/shared.js +6 -0
  78. package/dist/server/auth/providers/shared.js.map +1 -0
  79. package/dist/server/auth/providers/shared.test.d.ts +1 -0
  80. package/dist/server/auth/providers/shared.test.js +10 -0
  81. package/dist/server/auth/providers/shared.test.js.map +1 -0
  82. package/dist/server/auth/providers/stytch.d.ts +25 -0
  83. package/dist/server/auth/providers/stytch.js +16 -0
  84. package/dist/server/auth/providers/stytch.js.map +1 -0
  85. package/dist/server/auth/providers/verify-spy.d.ts +12 -0
  86. package/dist/server/auth/providers/verify-spy.js +21 -0
  87. package/dist/server/auth/providers/verify-spy.js.map +1 -0
  88. package/dist/server/auth/providers/workos.d.ts +33 -0
  89. package/dist/server/auth/providers/workos.js +15 -0
  90. package/dist/server/auth/providers/workos.js.map +1 -0
  91. package/dist/server/auth/security-schemes.d.ts +23 -0
  92. package/dist/server/auth/security-schemes.js +64 -0
  93. package/dist/server/auth/security-schemes.js.map +1 -0
  94. package/dist/server/auth/security-schemes.test.d.ts +1 -0
  95. package/dist/server/auth/security-schemes.test.js +99 -0
  96. package/dist/server/auth/security-schemes.test.js.map +1 -0
  97. package/dist/server/auth/setup.d.ts +3 -1
  98. package/dist/server/auth/setup.js +64 -25
  99. package/dist/server/auth/setup.js.map +1 -1
  100. package/dist/server/auth/setup.test.js +330 -19
  101. package/dist/server/auth/setup.test.js.map +1 -1
  102. package/dist/server/auth/verify.d.ts +40 -5
  103. package/dist/server/auth/verify.js +29 -4
  104. package/dist/server/auth/verify.js.map +1 -1
  105. package/dist/server/auth/verify.test.js +62 -0
  106. package/dist/server/auth/verify.test.js.map +1 -1
  107. package/dist/server/auth-extra.test-d.d.ts +1 -0
  108. package/dist/server/auth-extra.test-d.js +71 -0
  109. package/dist/server/auth-extra.test-d.js.map +1 -0
  110. package/dist/server/auth.d.ts +42 -5
  111. package/dist/server/auth.js +3 -4
  112. package/dist/server/auth.js.map +1 -1
  113. package/dist/server/content-helpers.d.ts +1 -1
  114. package/dist/server/content-helpers.js.map +1 -1
  115. package/dist/server/express.js +9 -28
  116. package/dist/server/express.js.map +1 -1
  117. package/dist/server/express.test.js +42 -7
  118. package/dist/server/express.test.js.map +1 -1
  119. package/dist/server/host.d.ts +2 -0
  120. package/dist/server/host.js +11 -0
  121. package/dist/server/host.js.map +1 -0
  122. package/dist/server/host.test.d.ts +1 -0
  123. package/dist/server/host.test.js +13 -0
  124. package/dist/server/host.test.js.map +1 -0
  125. package/dist/server/index.d.ts +14 -3
  126. package/dist/server/index.js +12 -2
  127. package/dist/server/index.js.map +1 -1
  128. package/dist/server/mcp-errors.test.d.ts +1 -0
  129. package/dist/server/mcp-errors.test.js +37 -0
  130. package/dist/server/mcp-errors.test.js.map +1 -0
  131. package/dist/server/middleware.d.ts +35 -10
  132. package/dist/server/middleware.js +33 -0
  133. package/dist/server/middleware.js.map +1 -1
  134. package/dist/server/middleware.test-d.js +1 -1
  135. package/dist/server/middleware.test-d.js.map +1 -1
  136. package/dist/server/middleware.test.js +38 -3
  137. package/dist/server/middleware.test.js.map +1 -1
  138. package/dist/server/register-tool.test.d.ts +1 -0
  139. package/dist/server/register-tool.test.js +25 -0
  140. package/dist/server/register-tool.test.js.map +1 -0
  141. package/dist/server/requestOrigin.d.ts +3 -2
  142. package/dist/server/requestOrigin.js +8 -8
  143. package/dist/server/requestOrigin.js.map +1 -1
  144. package/dist/server/server.d.ts +138 -50
  145. package/dist/server/server.js +280 -147
  146. package/dist/server/server.js.map +1 -1
  147. package/dist/server/skills-integration.test.d.ts +1 -0
  148. package/dist/server/skills-integration.test.js +79 -0
  149. package/dist/server/skills-integration.test.js.map +1 -0
  150. package/dist/server/skills.d.ts +27 -0
  151. package/dist/server/skills.js +188 -0
  152. package/dist/server/skills.js.map +1 -0
  153. package/dist/server/skills.test.d.ts +1 -0
  154. package/dist/server/skills.test.js +243 -0
  155. package/dist/server/skills.test.js.map +1 -0
  156. package/dist/server/tool-extra.test.d.ts +1 -0
  157. package/dist/server/tool-extra.test.js +72 -0
  158. package/dist/server/tool-extra.test.js.map +1 -0
  159. package/dist/server/view-name.test-d.d.ts +1 -0
  160. package/dist/server/view-name.test-d.js +8 -0
  161. package/dist/server/view-name.test-d.js.map +1 -0
  162. package/dist/server/view-resource-resolution.test.js +93 -12
  163. package/dist/server/view-resource-resolution.test.js.map +1 -1
  164. package/dist/server/viewsDevServer.js +4 -1
  165. package/dist/server/viewsDevServer.js.map +1 -1
  166. package/dist/standard-schema.d.ts +3 -0
  167. package/dist/standard-schema.js +2 -0
  168. package/dist/standard-schema.js.map +1 -0
  169. package/dist/test/utils.d.ts +5 -8
  170. package/dist/test/utils.js +8 -4
  171. package/dist/test/utils.js.map +1 -1
  172. package/dist/test/view.test.js +82 -114
  173. package/dist/test/view.test.js.map +1 -1
  174. package/dist/web/bridges/adaptor.d.ts +51 -0
  175. package/dist/web/bridges/adaptor.js +327 -0
  176. package/dist/web/bridges/adaptor.js.map +1 -0
  177. package/dist/web/bridges/adaptor.test.d.ts +1 -0
  178. package/dist/web/bridges/adaptor.test.js +225 -0
  179. package/dist/web/bridges/adaptor.test.js.map +1 -0
  180. package/dist/web/bridges/apps-sdk/bridge.d.ts +6 -2
  181. package/dist/web/bridges/apps-sdk/bridge.js +15 -4
  182. package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
  183. package/dist/web/bridges/apps-sdk/index.d.ts +0 -1
  184. package/dist/web/bridges/apps-sdk/index.js +0 -1
  185. package/dist/web/bridges/apps-sdk/index.js.map +1 -1
  186. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +1 -1
  187. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js +1 -1
  188. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -1
  189. package/dist/web/bridges/get-adaptor.d.ts +4 -4
  190. package/dist/web/bridges/get-adaptor.js +6 -11
  191. package/dist/web/bridges/get-adaptor.js.map +1 -1
  192. package/dist/web/bridges/get-adaptor.test.d.ts +1 -0
  193. package/dist/web/bridges/get-adaptor.test.js +32 -0
  194. package/dist/web/bridges/get-adaptor.test.js.map +1 -0
  195. package/dist/web/bridges/mcp-app/bridge.d.ts +13 -2
  196. package/dist/web/bridges/mcp-app/bridge.js +51 -7
  197. package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
  198. package/dist/web/bridges/mcp-app/bridge.test.d.ts +1 -0
  199. package/dist/web/bridges/mcp-app/bridge.test.js +17 -0
  200. package/dist/web/bridges/mcp-app/bridge.test.js.map +1 -0
  201. package/dist/web/bridges/mcp-app/index.d.ts +0 -1
  202. package/dist/web/bridges/mcp-app/index.js +0 -1
  203. package/dist/web/bridges/mcp-app/index.js.map +1 -1
  204. package/dist/web/bridges/mcp-app/types.d.ts +2 -0
  205. package/dist/web/bridges/mcp-app/types.js.map +1 -1
  206. package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +1 -1
  207. package/dist/web/bridges/mcp-app/use-mcp-app-context.js +1 -1
  208. package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -1
  209. package/dist/web/bridges/mcp-app/view-tools.test.js +5 -6
  210. package/dist/web/bridges/mcp-app/view-tools.test.js.map +1 -1
  211. package/dist/web/bridges/types.d.ts +27 -9
  212. package/dist/web/bridges/types.js +14 -1
  213. package/dist/web/bridges/types.js.map +1 -1
  214. package/dist/web/bridges/types.test.d.ts +1 -0
  215. package/dist/web/bridges/types.test.js +19 -0
  216. package/dist/web/bridges/types.test.js.map +1 -0
  217. package/dist/web/components/modal-provider.d.ts +1 -1
  218. package/dist/web/components/modal-provider.js +4 -3
  219. package/dist/web/components/modal-provider.js.map +1 -1
  220. package/dist/web/create-store.test.js +9 -6
  221. package/dist/web/create-store.test.js.map +1 -1
  222. package/dist/web/data-llm.d.ts +1 -1
  223. package/dist/web/data-llm.test.js +19 -6
  224. package/dist/web/data-llm.test.js.map +1 -1
  225. package/dist/web/hooks/index.d.ts +2 -1
  226. package/dist/web/hooks/index.js +2 -1
  227. package/dist/web/hooks/index.js.map +1 -1
  228. package/dist/web/hooks/test/utils.d.ts +2 -1
  229. package/dist/web/hooks/test/utils.js +1 -1
  230. package/dist/web/hooks/test/utils.js.map +1 -1
  231. package/dist/web/hooks/use-call-tool.test.js +39 -25
  232. package/dist/web/hooks/use-call-tool.test.js.map +1 -1
  233. package/dist/web/hooks/use-display-mode.d.ts +1 -1
  234. package/dist/web/hooks/use-display-mode.js +1 -1
  235. package/dist/web/hooks/use-display-mode.js.map +1 -1
  236. package/dist/web/hooks/use-display-mode.test.js +57 -21
  237. package/dist/web/hooks/use-display-mode.test.js.map +1 -1
  238. package/dist/web/hooks/use-download.d.ts +1 -3
  239. package/dist/web/hooks/use-download.js +1 -2
  240. package/dist/web/hooks/use-download.js.map +1 -1
  241. package/dist/web/hooks/use-download.test.js +16 -8
  242. package/dist/web/hooks/use-download.test.js.map +1 -1
  243. package/dist/web/hooks/use-files.test.js +11 -3
  244. package/dist/web/hooks/use-files.test.js.map +1 -1
  245. package/dist/web/hooks/use-host.d.ts +22 -0
  246. package/dist/web/hooks/use-host.js +30 -0
  247. package/dist/web/hooks/use-host.js.map +1 -0
  248. package/dist/web/hooks/use-host.test.d.ts +1 -0
  249. package/dist/web/hooks/use-host.test.js +59 -0
  250. package/dist/web/hooks/use-host.test.js.map +1 -0
  251. package/dist/web/hooks/use-open-external.test.js +14 -4
  252. package/dist/web/hooks/use-open-external.test.js.map +1 -1
  253. package/dist/web/hooks/use-register-view-tool.d.ts +2 -3
  254. package/dist/web/hooks/use-register-view-tool.js.map +1 -1
  255. package/dist/web/hooks/use-request-close.test.js +35 -40
  256. package/dist/web/hooks/use-request-close.test.js.map +1 -1
  257. package/dist/web/hooks/use-request-modal.test.js +11 -1
  258. package/dist/web/hooks/use-request-modal.test.js.map +1 -1
  259. package/dist/web/hooks/use-request-size.d.ts +1 -1
  260. package/dist/web/hooks/use-request-size.js +1 -1
  261. package/dist/web/hooks/use-request-size.js.map +1 -1
  262. package/dist/web/hooks/use-request-size.test.js +35 -53
  263. package/dist/web/hooks/use-request-size.test.js.map +1 -1
  264. package/dist/web/hooks/use-set-open-in-app-url.test.js +29 -9
  265. package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -1
  266. package/dist/web/hooks/use-tool-info.d.ts +1 -20
  267. package/dist/web/hooks/use-tool-info.js +0 -1
  268. package/dist/web/hooks/use-tool-info.js.map +1 -1
  269. package/dist/web/hooks/use-tool-info.test-d.js +0 -5
  270. package/dist/web/hooks/use-tool-info.test-d.js.map +1 -1
  271. package/dist/web/hooks/use-tool-info.test.js +37 -32
  272. package/dist/web/hooks/use-tool-info.test.js.map +1 -1
  273. package/dist/web/hooks/use-user.d.ts +3 -2
  274. package/dist/web/hooks/use-user.js +4 -3
  275. package/dist/web/hooks/use-user.js.map +1 -1
  276. package/dist/web/hooks/use-user.test.js +84 -36
  277. package/dist/web/hooks/use-user.test.js.map +1 -1
  278. package/dist/web/hooks/use-view-state.js +19 -9
  279. package/dist/web/hooks/use-view-state.js.map +1 -1
  280. package/dist/web/hooks/use-view-state.test.js +38 -3
  281. package/dist/web/hooks/use-view-state.test.js.map +1 -1
  282. package/dist/web/hooks/use-viewport.d.ts +20 -0
  283. package/dist/web/hooks/use-viewport.js +21 -0
  284. package/dist/web/hooks/use-viewport.js.map +1 -0
  285. package/dist/web/hooks/use-viewport.test.d.ts +1 -0
  286. package/dist/web/hooks/use-viewport.test.js +109 -0
  287. package/dist/web/hooks/use-viewport.test.js.map +1 -0
  288. package/dist/web/index.d.ts +1 -0
  289. package/dist/web/index.js.map +1 -1
  290. package/dist/web/mount-view.d.ts +3 -2
  291. package/dist/web/mount-view.js +3 -2
  292. package/dist/web/mount-view.js.map +1 -1
  293. package/dist/web/plugin/plugin-build.test.d.ts +1 -0
  294. package/dist/web/plugin/plugin-build.test.js +54 -0
  295. package/dist/web/plugin/plugin-build.test.js.map +1 -0
  296. package/dist/web/plugin/plugin.d.ts +6 -0
  297. package/dist/web/plugin/plugin.js +18 -7
  298. package/dist/web/plugin/plugin.js.map +1 -1
  299. package/dist/web/plugin/plugin.test.d.ts +1 -0
  300. package/dist/web/plugin/plugin.test.js +61 -0
  301. package/dist/web/plugin/plugin.test.js.map +1 -0
  302. package/package.json +28 -23
  303. package/dist/cli/resolve-views-dir.d.ts +0 -1
  304. package/dist/cli/resolve-views-dir.js.map +0 -1
  305. package/dist/web/bridges/apps-sdk/adaptor.d.ts +0 -29
  306. package/dist/web/bridges/apps-sdk/adaptor.js +0 -117
  307. package/dist/web/bridges/apps-sdk/adaptor.js.map +0 -1
  308. package/dist/web/bridges/mcp-app/adaptor.d.ts +0 -53
  309. package/dist/web/bridges/mcp-app/adaptor.js +0 -283
  310. package/dist/web/bridges/mcp-app/adaptor.js.map +0 -1
  311. package/dist/web/hooks/use-layout.d.ts +0 -24
  312. package/dist/web/hooks/use-layout.js +0 -25
  313. package/dist/web/hooks/use-layout.js.map +0 -1
  314. package/dist/web/hooks/use-layout.test.js +0 -96
  315. package/dist/web/hooks/use-layout.test.js.map +0 -1
  316. /package/dist/{web/hooks/use-layout.test.d.ts → cli/build-steps.test.d.ts} +0 -0
@@ -2,15 +2,18 @@ import crypto from "node:crypto";
2
2
  import { readFileSync } from "node:fs";
3
3
  import http from "node:http";
4
4
  import path from "node:path";
5
- import { Server as SdkServer, } from "@modelcontextprotocol/sdk/server/index.js";
6
- import { McpServer as McpServerBase } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { McpServer as McpServerBase, } from "@modelcontextprotocol/server";
7
6
  import { mergeWith, union } from "es-toolkit";
8
7
  import express, {} from "express";
8
+ import { warnOnLargeToolOutput } from "../context-warnings.js";
9
+ import { authToSecuritySchemes, evaluateSecuritySchemes, inBandChallengeResult, } from "./auth/security-schemes.js";
9
10
  import { setupOAuth } from "./auth/setup.js";
10
11
  import { createApp } from "./express.js";
12
+ import { hostFromUserAgent } from "./host.js";
11
13
  import { createMiddlewareEntry } from "./metric.js";
12
- import { buildMiddlewareChain, getHandlerMaps } from "./middleware.js";
14
+ import { buildMiddlewareChain, captureToolError, getHandlerMaps, } from "./middleware.js";
13
15
  import { resolveServerOrigin } from "./requestOrigin.js";
16
+ import { discoverSkills, registerSkills, SKILLS_EXTENSION_KEY, } from "./skills.js";
14
17
  import { templateHelper } from "./templateHelper.js";
15
18
  const mergeWithUnion = (target, source) => {
16
19
  return mergeWith(target, source, (targetVal, sourceVal) => {
@@ -19,6 +22,20 @@ const mergeWithUnion = (target, source) => {
19
22
  }
20
23
  });
21
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
+ }
22
39
  /**
23
40
  * Drop the query string from a `ui://` view URI, leaving the bare path. The
24
41
  * `?v=` cache key is the only query we append, so a plain split is enough and
@@ -92,6 +109,27 @@ let pendingBuildManifest = null;
92
109
  export function __setBuildManifest(manifest) {
93
110
  pendingBuildManifest = manifest;
94
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
+ }
95
133
  export class McpServer extends McpServerBaseOmitted {
96
134
  /**
97
135
  * The underlying Express app. Use this to extend the HTTP server with
@@ -111,6 +149,8 @@ export class McpServer extends McpServerBaseOmitted {
111
149
  customErrorMiddleware = [];
112
150
  mcpMiddlewareEntries = [];
113
151
  mcpMiddlewareApplied = false;
152
+ middlewareEntriesCache = null;
153
+ registrations = [];
114
154
  claimedViews = new Map();
115
155
  viewMetaBuilders = new Map();
116
156
  /**
@@ -123,14 +163,19 @@ export class McpServer extends McpServerBaseOmitted {
123
163
  viteManifest = null;
124
164
  serverInfo;
125
165
  serverOptions;
166
+ oauthEnabled = false;
167
+ resolveResourceMetadataUrl;
168
+ securitySchemesByTool = new Map();
126
169
  constructor(serverInfo, options, skybridgeOptions) {
127
- super(serverInfo, options);
170
+ const mergedOptions = withSkillsCapability(options, skybridgeOptions);
171
+ super(serverInfo, mergedOptions);
128
172
  this.serverInfo = serverInfo;
129
- this.serverOptions = options;
173
+ this.serverOptions = mergedOptions;
130
174
  this.express = express();
131
175
  this.express.use(express.json(skybridgeOptions?.json));
132
176
  if (skybridgeOptions?.oauth) {
133
- setupOAuth(this.express, skybridgeOptions.oauth);
177
+ this.oauthEnabled = true;
178
+ this.resolveResourceMetadataUrl = setupOAuth(this.express, skybridgeOptions.oauth, this.securitySchemesByTool);
134
179
  }
135
180
  // Pick up the manifest if `dist/__entry.js` primed it before importing
136
181
  // user code. Consume-once: clear after the first construction so a
@@ -141,6 +186,56 @@ export class McpServer extends McpServerBaseOmitted {
141
186
  this.setViteManifest(pendingBuildManifest);
142
187
  pendingBuildManifest = null;
143
188
  }
189
+ this.setupSkills(Boolean(skybridgeOptions?.skills));
190
+ }
191
+ registerResource(...args) {
192
+ return this.recordInherited("registerResource", args);
193
+ }
194
+ /**
195
+ * Register a prompt, recorded so every per-request instance carries it.
196
+ * Signature owned by Skybridge (not inherited) so a typed wrapper can land
197
+ * in a minor without a type-level break.
198
+ */
199
+ registerPrompt(name, config, cb) {
200
+ return this.recordInherited("registerPrompt", [
201
+ name,
202
+ config,
203
+ cb,
204
+ ]);
205
+ }
206
+ recordInherited(method, args) {
207
+ const base = McpServerBase.prototype[method];
208
+ let registered;
209
+ this.record((target) => {
210
+ const result = base.apply(target, args);
211
+ if (target === this) {
212
+ registered = result;
213
+ }
214
+ });
215
+ return registered;
216
+ }
217
+ skillRegistrar() {
218
+ const registerResource = this.registerResource.bind(this);
219
+ return {
220
+ registerResource,
221
+ server: {
222
+ setRequestHandler: ((...args) => {
223
+ this.recordRequestHandler(...args);
224
+ }),
225
+ },
226
+ };
227
+ }
228
+ setupSkills(enabled) {
229
+ const manifest = pendingSkillsManifest;
230
+ pendingSkillsManifest = null;
231
+ if (!enabled) {
232
+ return;
233
+ }
234
+ const skills = manifest ?? discoverSkills(SKILLS_DIR);
235
+ if (skills.length === 0) {
236
+ 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.`);
237
+ }
238
+ registerSkills(this.skillRegistrar(), skills);
144
239
  }
145
240
  use(pathOrHandler, ...handlers) {
146
241
  // Branching is load-bearing: Express's `app.use` overloads can't be
@@ -236,7 +331,7 @@ export class McpServer extends McpServerBaseOmitted {
236
331
  const result = (await next());
237
332
  for (const content of result.contents ?? []) {
238
333
  if (typeof content.uri === "string" &&
239
- stripQuery(content.uri) === path) {
334
+ stripQuery(content.uri) === stripQuery(canonical)) {
240
335
  content.uri = requested;
241
336
  }
242
337
  }
@@ -249,17 +344,39 @@ export class McpServer extends McpServerBaseOmitted {
249
344
  }
250
345
  },
251
346
  };
347
+ // ChatGPT reads `securitySchemes` at the tool descriptor top level (SEP-1488,
348
+ // still Draft), but the SDK's registerTool strips unknown top-level fields, so
349
+ // it's stashed in `_meta` at registration. This restores it to the top level
350
+ // on tools/list output. Remove once SEP-1488 lands and the SDK preserves it.
351
+ // { name: "checkout", _meta: { securitySchemes: [{ type: "oauth2" }] } }
352
+ // -> { name: "checkout", _meta: {…}, securitySchemes: [{ type: "oauth2" }] }
353
+ const toolsListSecuritySchemesEntry = {
354
+ filter: "tools/list",
355
+ handler: async (_req, _extra, next) => {
356
+ const result = (await next());
357
+ for (const tool of result.tools) {
358
+ const schemes = tool._meta?.securitySchemes;
359
+ if (schemes && !("securitySchemes" in tool)) {
360
+ tool.securitySchemes = schemes;
361
+ }
362
+ }
363
+ return result;
364
+ },
365
+ };
252
366
  const monitoringEntry = createMiddlewareEntry();
253
367
  const entries = [
254
368
  ...(monitoringEntry ? [monitoringEntry] : []),
255
369
  viewListMetaEntry,
256
370
  viewReadResolveEntry,
371
+ toolsListSecuritySchemesEntry,
257
372
  ...this.mcpMiddlewareEntries,
258
373
  ];
259
- if (entries.length === 0) {
260
- return;
261
- }
262
- const { requestHandlers, notificationHandlers } = getHandlerMaps(this.server);
374
+ this.middlewareEntriesCache = entries;
375
+ this.instrumentHandlers(this.server);
376
+ }
377
+ instrumentHandlers(server) {
378
+ const entries = this.middlewareEntriesCache ?? [];
379
+ const { requestHandlers, notificationHandlers } = getHandlerMaps(server);
263
380
  const instrumentMap = (map, isNotification) => {
264
381
  for (const [method, handler] of map) {
265
382
  map.set(method, buildMiddlewareChain(method, isNotification, handler, entries));
@@ -283,23 +400,36 @@ export class McpServer extends McpServerBaseOmitted {
283
400
  return McpServerBase.prototype.connect.call(this, transport);
284
401
  }
285
402
  /**
286
- * Per-request stateless connect. The SDK's `Protocol` only allows one
287
- * transport per instance, so we can't reuse this `McpServer` across
288
- * concurrent requests. The SDK's idiomatic fix is a `() => McpServer`
289
- * factory, but that would break Skybridge's singleton API — so instead
290
- * we build a fresh underlying `Server` per request and share the main
291
- * server's handler maps by reference. The cast is unavoidable: there's
292
- * no public API to inject handler maps. `getHandlerMaps` validates the
293
- * read side and fails fast on SDK field renames.
403
+ * Build a fresh server for one stateless HTTP request, as
404
+ * `createMcpHandler`'s factory contract requires: every registration is
405
+ * replayed onto the new instance so the SDK's handler closures belong to
406
+ * the instance whose protocol era it stamps. Sharing the singleton's
407
+ * handler maps instead would bind them to an instance that is never
408
+ * marked, pinning every request to the 2025 codec and letting concurrent
409
+ * callers overwrite each other's negotiated version.
294
410
  */
295
- async connectStatelessTransport(transport) {
411
+ createStatelessServerInstance() {
296
412
  this.applyMcpMiddleware();
297
- const { requestHandlers, notificationHandlers } = getHandlerMaps(this.server);
298
- const fresh = new SdkServer(this.serverInfo, this.serverOptions);
299
- const target = fresh;
300
- target._requestHandlers = requestHandlers;
301
- target._notificationHandlers = notificationHandlers;
302
- await fresh.connect(transport);
413
+ const fresh = new McpServerBase(this.serverInfo, this.serverOptions);
414
+ for (const apply of this.registrations) {
415
+ apply(fresh);
416
+ }
417
+ this.instrumentHandlers(fresh.server);
418
+ return fresh.server;
419
+ }
420
+ /**
421
+ * Apply an SDK registration to the singleton and remember it, so each
422
+ * per-request instance can be built with the same surface.
423
+ */
424
+ record(apply) {
425
+ this.registrations.push(apply);
426
+ apply(this);
427
+ }
428
+ /** Records a raw request handler (used for methods the SDK has no sugar for). */
429
+ recordRequestHandler(...args) {
430
+ this.record((target) => {
431
+ target.server.setRequestHandler(...args);
432
+ });
303
433
  }
304
434
  /**
305
435
  * Start the HTTP server. Listens on `process.env.__PORT` (default `3000`),
@@ -374,15 +504,16 @@ export class McpServer extends McpServerBaseOmitted {
374
504
  }
375
505
  this.claimedViews.set(component, toolName);
376
506
  }
377
- resolveViewRequestContext(extra) {
507
+ resolveViewRequestContext(ctx) {
378
508
  const isProduction = process.env.NODE_ENV === "production";
379
- const headers = extra?.requestInfo?.headers || {};
380
- const header = (key) => {
381
- const val = headers[key];
382
- return Array.isArray(val) ? val[0] : val;
383
- };
384
- const isClaude = header("user-agent") === "Claude-User";
509
+ const header = (key) => ctx?.http?.req?.headers.get(key) ?? undefined;
510
+ const isClaude = hostFromUserAgent(header("user-agent")) === "claude";
385
511
  const serverUrl = resolveServerOrigin(header);
512
+ // Path prefix the proxy routed this request under (e.g. `foo.com/v1`). Read
513
+ // per-request so one process can serve many hosts/prefixes at once: the
514
+ // origin is recovered from x-forwarded-host, the prefix from
515
+ // x-forwarded-prefix. Empty when served at the origin root.
516
+ const assetsBasePath = normalizeForwardedPrefix(header("x-forwarded-prefix"));
386
517
  const connectDomains = [serverUrl];
387
518
  if (!isProduction) {
388
519
  const wsUrl = new URL(serverUrl);
@@ -391,7 +522,7 @@ export class McpServer extends McpServerBaseOmitted {
391
522
  }
392
523
  let contentMetaOverrides = {};
393
524
  if (isClaude) {
394
- const pathname = extra?.requestInfo?.url?.pathname ?? "";
525
+ const pathname = ctx?.http?.req ? new URL(ctx.http.req.url).pathname : "";
395
526
  const rawUrl = header("x-alpic-forwarded-url") ?? `${serverUrl}${pathname}`;
396
527
  // Strip a lone trailing slash so the hash matches the connector URL
397
528
  // as registered with Claude (which has no trailing slash on bare origins).
@@ -403,114 +534,77 @@ export class McpServer extends McpServerBaseOmitted {
403
534
  .slice(0, 32);
404
535
  contentMetaOverrides = { domain: `${hash}.claudemcpcontent.com` };
405
536
  }
406
- return { serverUrl, connectDomains, contentMetaOverrides };
537
+ return { serverUrl, assetsBasePath, connectDomains, contentMetaOverrides };
407
538
  }
408
539
  registerViewResources(toolName, view, toolMeta) {
409
- const hosts = view.hosts ?? ["apps-sdk", "mcp-app"];
410
540
  // Append a content-derived version param so hosts (e.g. ChatGPT) bust
411
541
  // their cache when the bundle changes, but keep the URI stable across
412
542
  // `tools/list` calls when the bundle hasn't changed.
413
543
  const versionParam = this.computeViewVersionParam(view.component);
414
- if (hosts.includes("apps-sdk")) {
415
- const viewResource = {
416
- hostType: "apps-sdk",
417
- uri: `ui://views/apps-sdk/${view.component}.html${versionParam}`,
418
- mimeType: "text/html+skybridge",
419
- buildContentMeta: ({ resourceDomains, connectDomains, domain }, overrides) => {
420
- const defaults = {
421
- "openai/widgetCSP": {
422
- resource_domains: resourceDomains,
423
- connect_domains: connectDomains,
424
- },
425
- "openai/widgetDomain": domain,
426
- "openai/widgetDescription": view.description,
427
- };
428
- const fromView = {
429
- "openai/widgetCSP": {
430
- resource_domains: view.csp?.resourceDomains,
431
- connect_domains: view.csp?.connectDomains,
432
- frame_domains: view.csp?.frameDomains,
433
- redirect_domains: view.csp?.redirectDomains,
434
- },
435
- "openai/widgetDomain": view.domain,
436
- "openai/widgetPrefersBorder": view.prefersBorder,
437
- };
438
- const base = mergeWithUnion(mergeWithUnion(defaults, fromView), {
439
- "openai/widgetDomain": overrides.domain,
440
- });
441
- if (view._meta) {
442
- return { ...base, ...view._meta };
443
- }
444
- return base;
445
- },
446
- };
447
- this.registerViewResource({
448
- name: toolName,
449
- viewResource,
450
- view,
451
- });
452
- toolMeta["openai/outputTemplate"] = viewResource.uri;
453
- }
454
- if (hosts.includes("mcp-app")) {
455
- const viewResource = {
456
- hostType: "mcp-app",
457
- uri: `ui://views/ext-apps/${view.component}.html${versionParam}`,
458
- mimeType: "text/html;profile=mcp-app",
459
- buildContentMeta: ({ resourceDomains, connectDomains, domain, baseUriDomains }, overrides) => {
460
- const defaults = {
461
- ui: {
462
- csp: {
463
- resourceDomains,
464
- connectDomains,
465
- baseUriDomains,
466
- },
467
- domain,
544
+ const viewResource = {
545
+ hostType: "mcp-app",
546
+ uri: `ui://views/ext-apps/${view.component}.html${versionParam}`,
547
+ mimeType: "text/html;profile=mcp-app",
548
+ buildContentMeta: ({ resourceDomains, connectDomains, domain, baseUriDomains }, overrides) => {
549
+ const defaults = {
550
+ ui: {
551
+ csp: {
552
+ resourceDomains,
553
+ connectDomains,
554
+ baseUriDomains,
468
555
  },
469
- };
470
- const fromView = {
471
- ui: {
472
- ...(view.description && { description: view.description }),
473
- ...(view.prefersBorder !== undefined && {
474
- prefersBorder: view.prefersBorder,
556
+ domain,
557
+ },
558
+ };
559
+ const fromView = {
560
+ ui: {
561
+ ...(view.description && { description: view.description }),
562
+ ...(view.prefersBorder !== undefined && {
563
+ prefersBorder: view.prefersBorder,
564
+ }),
565
+ ...(view.domain && { domain: view.domain }),
566
+ csp: {
567
+ ...(view.csp?.resourceDomains && {
568
+ resourceDomains: view.csp.resourceDomains,
569
+ }),
570
+ ...(view.csp?.connectDomains && {
571
+ connectDomains: view.csp.connectDomains,
572
+ }),
573
+ ...(view.csp?.frameDomains && {
574
+ frameDomains: view.csp.frameDomains,
575
+ }),
576
+ ...(view.csp?.baseUriDomains && {
577
+ baseUriDomains: view.csp.baseUriDomains,
475
578
  }),
476
- ...(view.domain && { domain: view.domain }),
477
- csp: {
478
- ...(view.csp?.resourceDomains && {
479
- resourceDomains: view.csp.resourceDomains,
480
- }),
481
- ...(view.csp?.connectDomains && {
482
- connectDomains: view.csp.connectDomains,
483
- }),
484
- ...(view.csp?.frameDomains && {
485
- frameDomains: view.csp.frameDomains,
486
- }),
487
- ...(view.csp?.baseUriDomains && {
488
- baseUriDomains: view.csp.baseUriDomains,
489
- }),
490
- ...(view.csp?.redirectDomains && {
491
- redirectDomains: view.csp.redirectDomains,
492
- }),
493
- },
494
579
  },
495
- };
496
- const base = mergeWithUnion(mergeWithUnion(defaults, fromView), {
497
- ui: overrides,
498
- });
499
- if (view._meta) {
500
- return { ...base, ...view._meta };
501
- }
502
- return base;
503
- },
504
- };
505
- this.registerViewResource({
506
- name: toolName,
507
- viewResource,
508
- view,
509
- });
510
- // @ts-expect-error - For backwards compatibility with Claude current implementation of the specs
511
- toolMeta["ui/resourceUri"] = viewResource.uri;
512
- toolMeta.ui = { ...toolMeta.ui, resourceUri: viewResource.uri };
513
- }
580
+ },
581
+ };
582
+ const ui = mergeWithUnion(mergeWithUnion(defaults, fromView), {
583
+ ui: overrides,
584
+ });
585
+ const base = {
586
+ ...ui,
587
+ ...(view.description && {
588
+ "openai/widgetDescription": view.description,
589
+ }),
590
+ ...(view.csp?.redirectDomains && {
591
+ "openai/widgetCSP": { redirect_domains: view.csp.redirectDomains },
592
+ }),
593
+ };
594
+ if (view._meta) {
595
+ return { ...base, ...view._meta };
596
+ }
597
+ return base;
598
+ },
599
+ };
600
+ this.registerViewResource({ name: toolName, viewResource, view });
601
+ // Advertise via the MCP Apps standard pointer only — ChatGPT renders from
602
+ // ui.resourceUri (verified), and not emitting openai/outputTemplate lets us
603
+ // retire the legacy apps-sdk resource later. The legacy apps-sdk URL is still
604
+ // served (see registerViewResource) so already-published apps keep resolving.
605
+ // @ts-expect-error - For backwards compatibility with Claude current implementation of the specs
606
+ toolMeta["ui/resourceUri"] = viewResource.uri;
607
+ toolMeta.ui = { ...toolMeta.ui, resourceUri: viewResource.uri };
514
608
  }
515
609
  registerViewResource({ name, viewResource, view, }) {
516
610
  const { hostType, uri: viewUri, mimeType, buildContentMeta } = viewResource;
@@ -525,19 +619,24 @@ export class McpServer extends McpServerBaseOmitted {
525
619
  };
526
620
  this.viewMetaBuilders.set(viewUri, buildMeta);
527
621
  this.viewUriByPath.set(stripQuery(viewUri), viewUri);
622
+ this.serveLegacyAppsSdkUrl(view.component, viewUri);
528
623
  this.registerResource(name, viewUri, { description: view.description }, async (uri, extra) => {
529
624
  const isProduction = process.env.NODE_ENV === "production";
530
- const { serverUrl } = this.resolveViewRequestContext(extra);
625
+ const { serverUrl, assetsBasePath } = this.resolveViewRequestContext(extra);
626
+ // The view resolves all assets (template imports + runtime lazy chunks
627
+ // via `window.skybridge.serverUrl`) against this base, so it carries the
628
+ // proxy path prefix. CSP domains in `buildMeta` stay the bare origin.
629
+ const viewBase = `${serverUrl}${assetsBasePath}`;
531
630
  const html = isProduction
532
631
  ? templateHelper.renderProduction({
533
632
  hostType,
534
- serverUrl,
633
+ serverUrl: viewBase,
535
634
  viewFile: this.lookupViewFile(view.component),
536
635
  styleFile: this.lookupDistFile("style.css") ?? "",
537
636
  })
538
637
  : templateHelper.renderDevelopment({
539
638
  hostType,
540
- serverUrl,
639
+ serverUrl: viewBase,
541
640
  viewName: view.component,
542
641
  });
543
642
  return {
@@ -547,9 +646,29 @@ export class McpServer extends McpServerBaseOmitted {
547
646
  };
548
647
  });
549
648
  }
550
- wrapHandler(cb, { attachViewUUID }) {
649
+ serveLegacyAppsSdkUrl(component, canonicalUri) {
650
+ this.viewUriByPath.set(`ui://views/apps-sdk/${component}.html`, canonicalUri);
651
+ this.viewUriByPath.set(`ui://widgets/apps-sdk/${component}.html`, canonicalUri);
652
+ this.viewUriByPath.set(`ui://widgets/ext-apps/${component}.html`, canonicalUri);
653
+ }
654
+ decorateToolHandler(cb, { attachViewUUID, securitySchemes, toolName, }) {
551
655
  return async (args, extra) => {
552
- const result = await cb(args, extra);
656
+ if (this.oauthEnabled) {
657
+ const failure = evaluateSecuritySchemes(securitySchemes, extra.http?.authInfo);
658
+ if (failure) {
659
+ const header = (key) => extra.http?.req?.headers.get(key) ?? undefined;
660
+ return inBandChallengeResult(failure, this.resolveResourceMetadataUrl?.(header));
661
+ }
662
+ }
663
+ let result;
664
+ try {
665
+ result = await cb(args, extra);
666
+ }
667
+ catch (error) {
668
+ captureToolError(extra, error);
669
+ throw error;
670
+ }
671
+ warnOnLargeToolOutput(result, toolName);
553
672
  return {
554
673
  ...result,
555
674
  content: normalizeContent(result.content),
@@ -611,16 +730,22 @@ export class McpServer extends McpServerBaseOmitted {
611
730
  }
612
731
  return JSON.parse(readFileSync(path.join(process.cwd(), "dist", "assets", ".vite", "manifest.json"), "utf-8"));
613
732
  }
614
- registerTool(...args) {
733
+ registerTool(rawConfig, rawCb) {
615
734
  const baseFn = McpServerBase.prototype.registerTool;
616
- if (typeof args[0] === "string") {
617
- baseFn.call(this, args[0], args[1], args[2]);
618
- return this;
735
+ const config = rawConfig;
736
+ const cb = rawCb;
737
+ const { name, view, auth, securitySchemes: rawSecuritySchemes, _meta: userToolMeta, ...toolFields } = config;
738
+ const authNeedsProvider = auth !== undefined &&
739
+ (!auth.allowsAnonymous || Boolean(auth.scopes?.length));
740
+ if (rawSecuritySchemes === undefined &&
741
+ authNeedsProvider &&
742
+ !this.oauthEnabled) {
743
+ throw new Error(`Tool "${name}" sets \`auth: ${JSON.stringify(auth)}\` but the server has no \`oauth\` provider configured.`);
619
744
  }
620
- const config = args[0];
621
- const cb = args[1];
622
- const { name, view, securitySchemes, _meta: userToolMeta, ...toolFields } = config;
745
+ const securitySchemes = rawSecuritySchemes ??
746
+ (auth && this.oauthEnabled ? authToSecuritySchemes(auth) : undefined);
623
747
  const toolMeta = { ...userToolMeta };
748
+ this.securitySchemesByTool.set(name, securitySchemes);
624
749
  if (securitySchemes) {
625
750
  // SEP-1488 puts `securitySchemes` at the top level of the tool
626
751
  // descriptor, but the SDK's `registerTool` drops unknown top-level
@@ -633,8 +758,16 @@ export class McpServer extends McpServerBaseOmitted {
633
758
  this.enforceOneToolPerView(view.component, name);
634
759
  this.registerViewResources(name, view, toolMeta);
635
760
  }
636
- const wrappedCb = this.wrapHandler(cb, { attachViewUUID: Boolean(view) });
637
- baseFn.call(this, name, { ...toolFields, _meta: toolMeta }, wrappedCb);
761
+ const wrappedCb = this.decorateToolHandler(cb, {
762
+ attachViewUUID: Boolean(view),
763
+ securitySchemes,
764
+ toolName: name,
765
+ });
766
+ this.record((target) => {
767
+ baseFn.call(target, name, { ...toolFields, _meta: toolMeta }, toolFields.inputSchema === undefined
768
+ ? (extra) => wrappedCb({}, extra)
769
+ : wrappedCb);
770
+ });
638
771
  return this;
639
772
  }
640
773
  }