skybridge 0.0.0-dev.b9f65be → 0.0.0-dev.b9f85b3
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.
- package/README.md +3 -0
- package/dist/cli/build-helpers.d.ts +7 -1
- package/dist/cli/build-helpers.js +58 -6
- package/dist/cli/build-helpers.js.map +1 -1
- package/dist/cli/build-helpers.test.js +89 -1
- package/dist/cli/build-helpers.test.js.map +1 -1
- package/dist/cli/build-steps.d.ts +2 -0
- package/dist/cli/build-steps.js +73 -0
- package/dist/cli/build-steps.js.map +1 -0
- package/dist/cli/build-steps.test.js +52 -0
- package/dist/cli/build-steps.test.js.map +1 -0
- package/dist/cli/detect-port.d.ts +2 -2
- package/dist/cli/detect-port.js +8 -20
- package/dist/cli/detect-port.js.map +1 -1
- package/dist/cli/header.d.ts +1 -1
- package/dist/cli/resolve-skybridge-config.d.ts +5 -0
- package/dist/cli/{resolve-views-dir.js → resolve-skybridge-config.js} +4 -3
- package/dist/cli/resolve-skybridge-config.js.map +1 -0
- package/dist/cli/run-plain.d.ts +18 -0
- package/dist/cli/run-plain.js +89 -0
- package/dist/cli/run-plain.js.map +1 -0
- package/dist/cli/use-execute-steps.d.ts +1 -1
- package/dist/cli/use-nodemon.d.ts +14 -0
- package/dist/cli/use-nodemon.js +81 -62
- package/dist/cli/use-nodemon.js.map +1 -1
- package/dist/cli/use-typescript-check.d.ts +8 -2
- package/dist/cli/use-typescript-check.js +70 -67
- package/dist/cli/use-typescript-check.js.map +1 -1
- package/dist/commands/build.d.ts +0 -3
- package/dist/commands/build.js +5 -56
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/dev.d.ts +1 -0
- package/dist/commands/dev.js +35 -3
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/start.js +7 -1
- package/dist/commands/start.js.map +1 -1
- package/dist/context-warnings.d.ts +5 -0
- package/dist/context-warnings.js +31 -0
- package/dist/context-warnings.js.map +1 -0
- package/dist/context-warnings.test.js +28 -0
- package/dist/context-warnings.test.js.map +1 -0
- package/dist/server/app.d.ts +175 -0
- package/dist/server/app.js +275 -0
- package/dist/server/app.js.map +1 -0
- package/dist/server/auth/discovery.d.ts +33 -0
- package/dist/server/auth/discovery.js +56 -0
- package/dist/server/auth/discovery.js.map +1 -0
- package/dist/server/auth/discovery.test.js +93 -0
- package/dist/server/auth/discovery.test.js.map +1 -0
- package/dist/server/auth/index.d.ts +30 -0
- package/dist/server/auth/index.js +2 -0
- package/dist/server/auth/index.js.map +1 -0
- package/dist/server/auth/providers/auth0.d.ts +40 -0
- package/dist/server/auth/providers/auth0.js +31 -0
- package/dist/server/auth/providers/auth0.js.map +1 -0
- package/dist/server/auth/providers/auth0.test.js +50 -0
- package/dist/server/auth/providers/auth0.test.js.map +1 -0
- package/dist/server/auth/providers/authplane.d.ts +49 -0
- package/dist/server/auth/providers/authplane.js +57 -0
- package/dist/server/auth/providers/authplane.js.map +1 -0
- package/dist/server/auth/providers/authplane.test.d.ts +1 -0
- package/dist/server/auth/providers/authplane.test.js +140 -0
- package/dist/server/auth/providers/authplane.test.js.map +1 -0
- package/dist/server/auth/providers/clerk.d.ts +51 -0
- package/dist/server/auth/providers/clerk.js +19 -0
- package/dist/server/auth/providers/clerk.js.map +1 -0
- package/dist/server/auth/providers/clerk.test.d.ts +1 -0
- package/dist/server/auth/providers/clerk.test.js +30 -0
- package/dist/server/auth/providers/clerk.test.js.map +1 -0
- package/dist/server/auth/providers/custom.d.ts +40 -0
- package/dist/server/auth/providers/custom.js +56 -0
- package/dist/server/auth/providers/custom.js.map +1 -0
- package/dist/server/auth/providers/custom.test.d.ts +1 -0
- package/dist/server/auth/providers/custom.test.js +158 -0
- package/dist/server/auth/providers/custom.test.js.map +1 -0
- package/dist/server/auth/providers/descope.d.ts +44 -0
- package/dist/server/auth/providers/descope.js +44 -0
- package/dist/server/auth/providers/descope.js.map +1 -0
- package/dist/server/auth/providers/descope.test.d.ts +1 -0
- package/dist/server/auth/providers/descope.test.js +62 -0
- package/dist/server/auth/providers/descope.test.js.map +1 -0
- package/dist/server/auth/providers/shared.d.ts +2 -0
- package/dist/server/auth/providers/shared.js +6 -0
- package/dist/server/auth/providers/shared.js.map +1 -0
- package/dist/server/auth/providers/shared.test.d.ts +1 -0
- package/dist/server/auth/providers/shared.test.js +10 -0
- package/dist/server/auth/providers/shared.test.js.map +1 -0
- package/dist/server/auth/providers/stytch.d.ts +25 -0
- package/dist/server/auth/providers/stytch.js +16 -0
- package/dist/server/auth/providers/stytch.js.map +1 -0
- package/dist/server/auth/providers/verify-spy.d.ts +12 -0
- package/dist/server/auth/providers/verify-spy.js +21 -0
- package/dist/server/auth/providers/verify-spy.js.map +1 -0
- package/dist/server/auth/providers/workos.d.ts +33 -0
- package/dist/server/auth/providers/workos.js +15 -0
- package/dist/server/auth/providers/workos.js.map +1 -0
- package/dist/server/auth/security-schemes.d.ts +23 -0
- package/dist/server/auth/security-schemes.js +64 -0
- package/dist/server/auth/security-schemes.js.map +1 -0
- package/dist/server/auth/security-schemes.test.d.ts +1 -0
- package/dist/server/auth/security-schemes.test.js +99 -0
- package/dist/server/auth/security-schemes.test.js.map +1 -0
- package/dist/server/auth/setup.d.ts +6 -0
- package/dist/server/auth/setup.js +91 -0
- package/dist/server/auth/setup.js.map +1 -0
- package/dist/server/auth/setup.test.d.ts +1 -0
- package/dist/server/auth/setup.test.js +512 -0
- package/dist/server/auth/setup.test.js.map +1 -0
- package/dist/server/auth/verify.d.ts +46 -0
- package/dist/server/auth/verify.js +62 -0
- package/dist/server/auth/verify.js.map +1 -0
- package/dist/server/auth/verify.test.d.ts +1 -0
- package/dist/server/auth/verify.test.js +149 -0
- package/dist/server/auth/verify.test.js.map +1 -0
- package/dist/server/auth-extra.test-d.js +71 -0
- package/dist/server/auth-extra.test-d.js.map +1 -0
- package/dist/server/auth.d.ts +42 -5
- package/dist/server/auth.js +3 -4
- package/dist/server/auth.js.map +1 -1
- package/dist/server/build-manifest.test.d.ts +1 -0
- package/dist/server/build-manifest.test.js +28 -0
- package/dist/server/build-manifest.test.js.map +1 -0
- package/dist/server/content-helpers.d.ts +1 -1
- package/dist/server/content-helpers.js.map +1 -1
- package/dist/server/express.d.ts +23 -3
- package/dist/server/express.js +37 -32
- package/dist/server/express.js.map +1 -1
- package/dist/server/express.test.js +154 -95
- package/dist/server/express.test.js.map +1 -1
- package/dist/server/factory-loader.test.d.ts +1 -0
- package/dist/server/factory-loader.test.js +81 -0
- package/dist/server/factory-loader.test.js.map +1 -0
- package/dist/server/file-ref.d.ts +1 -1
- package/dist/server/file-ref.js +1 -1
- package/dist/server/file-ref.js.map +1 -1
- package/dist/server/host.d.ts +2 -0
- package/dist/server/host.js +11 -0
- package/dist/server/host.js.map +1 -0
- package/dist/server/host.test.d.ts +1 -0
- package/dist/server/host.test.js +13 -0
- package/dist/server/host.test.js.map +1 -0
- package/dist/server/index.d.ts +16 -3
- package/dist/server/index.js +13 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp-errors.test.d.ts +1 -0
- package/dist/server/mcp-errors.test.js +37 -0
- package/dist/server/mcp-errors.test.js.map +1 -0
- package/dist/server/middleware.d.ts +35 -10
- package/dist/server/middleware.js +33 -0
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/middleware.test-d.js +1 -1
- package/dist/server/middleware.test-d.js.map +1 -1
- package/dist/server/middleware.test.js +190 -131
- package/dist/server/middleware.test.js.map +1 -1
- package/dist/server/register-tool.test.d.ts +1 -0
- package/dist/server/register-tool.test.js +23 -0
- package/dist/server/register-tool.test.js.map +1 -0
- package/dist/server/requestOrigin.d.ts +7 -0
- package/dist/server/requestOrigin.js +25 -0
- package/dist/server/requestOrigin.js.map +1 -0
- package/dist/server/server.d.ts +167 -122
- package/dist/server/server.js +346 -330
- package/dist/server/server.js.map +1 -1
- package/dist/server/skills-integration.test.d.ts +1 -0
- package/dist/server/skills-integration.test.js +79 -0
- package/dist/server/skills-integration.test.js.map +1 -0
- package/dist/server/skills.d.ts +27 -0
- package/dist/server/skills.js +188 -0
- package/dist/server/skills.js.map +1 -0
- package/dist/server/skills.test.d.ts +1 -0
- package/dist/server/skills.test.js +243 -0
- package/dist/server/skills.test.js.map +1 -0
- package/dist/server/templateHelper.d.ts +0 -3
- package/dist/server/templateHelper.js.map +1 -1
- package/dist/server/templates.generated.js +4 -8
- package/dist/server/templates.generated.js.map +1 -1
- package/dist/server/tool-extra.test.d.ts +1 -0
- package/dist/server/tool-extra.test.js +72 -0
- package/dist/server/tool-extra.test.js.map +1 -0
- package/dist/server/view-name.test-d.d.ts +1 -0
- package/dist/server/view-name.test-d.js +8 -0
- package/dist/server/view-name.test-d.js.map +1 -0
- package/dist/server/view-resource-resolution.test.d.ts +6 -0
- package/dist/server/view-resource-resolution.test.js +172 -0
- package/dist/server/view-resource-resolution.test.js.map +1 -0
- package/dist/server/viewsDevServer.js +4 -1
- package/dist/server/viewsDevServer.js.map +1 -1
- package/dist/test/utils.d.ts +9 -11
- package/dist/test/utils.js +18 -13
- package/dist/test/utils.js.map +1 -1
- package/dist/test/view.test.js +88 -120
- package/dist/test/view.test.js.map +1 -1
- package/dist/views/index.d.ts +10 -0
- package/dist/views/index.js +3 -0
- package/dist/views/index.js.map +1 -0
- package/dist/{web/plugin → views}/scan-views.js +1 -1
- package/dist/views/scan-views.js.map +1 -0
- package/dist/views/scan-views.test.d.ts +1 -0
- package/dist/views/scan-views.test.js.map +1 -0
- package/dist/views/validate-view.d.ts +1 -0
- package/dist/{web/plugin → views}/validate-view.js +2 -2
- package/dist/views/validate-view.js.map +1 -0
- package/dist/views/validate-view.test.d.ts +1 -0
- package/dist/views/validate-view.test.js.map +1 -0
- package/dist/web/bridges/adaptor.d.ts +51 -0
- package/dist/web/bridges/adaptor.js +327 -0
- package/dist/web/bridges/adaptor.js.map +1 -0
- package/dist/web/bridges/adaptor.test.d.ts +1 -0
- package/dist/web/bridges/adaptor.test.js +225 -0
- package/dist/web/bridges/adaptor.test.js.map +1 -0
- package/dist/web/bridges/apps-sdk/bridge.d.ts +6 -2
- package/dist/web/bridges/apps-sdk/bridge.js +15 -4
- package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
- package/dist/web/bridges/apps-sdk/index.d.ts +0 -1
- package/dist/web/bridges/apps-sdk/index.js +0 -1
- package/dist/web/bridges/apps-sdk/index.js.map +1 -1
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +1 -1
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js +1 -1
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -1
- package/dist/web/bridges/get-adaptor.d.ts +4 -4
- package/dist/web/bridges/get-adaptor.js +6 -11
- package/dist/web/bridges/get-adaptor.js.map +1 -1
- package/dist/web/bridges/get-adaptor.test.d.ts +1 -0
- package/dist/web/bridges/get-adaptor.test.js +32 -0
- package/dist/web/bridges/get-adaptor.test.js.map +1 -0
- package/dist/web/bridges/mcp-app/bridge.d.ts +15 -3
- package/dist/web/bridges/mcp-app/bridge.js +73 -8
- package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
- package/dist/web/bridges/mcp-app/bridge.test.d.ts +1 -0
- package/dist/web/bridges/mcp-app/bridge.test.js +17 -0
- package/dist/web/bridges/mcp-app/bridge.test.js.map +1 -0
- package/dist/web/bridges/mcp-app/index.d.ts +0 -1
- package/dist/web/bridges/mcp-app/index.js +0 -1
- package/dist/web/bridges/mcp-app/index.js.map +1 -1
- package/dist/web/bridges/mcp-app/types.d.ts +2 -0
- package/dist/web/bridges/mcp-app/types.js.map +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js.map +1 -1
- package/dist/web/bridges/mcp-app/view-tools.test.d.ts +1 -0
- package/dist/web/bridges/mcp-app/view-tools.test.js +143 -0
- package/dist/web/bridges/mcp-app/view-tools.test.js.map +1 -0
- package/dist/web/bridges/types.d.ts +55 -7
- package/dist/web/bridges/types.js +14 -1
- package/dist/web/bridges/types.js.map +1 -1
- package/dist/web/bridges/types.test.d.ts +1 -0
- package/dist/web/bridges/types.test.js +19 -0
- package/dist/web/bridges/types.test.js.map +1 -0
- package/dist/web/components/modal-provider.d.ts +1 -1
- package/dist/web/components/modal-provider.js +4 -3
- package/dist/web/components/modal-provider.js.map +1 -1
- package/dist/web/create-store.test.js +10 -7
- package/dist/web/create-store.test.js.map +1 -1
- package/dist/web/data-llm.d.ts +1 -1
- package/dist/web/data-llm.test.js +20 -7
- package/dist/web/data-llm.test.js.map +1 -1
- package/dist/web/generate-helpers.d.ts +13 -12
- package/dist/web/generate-helpers.js +10 -9
- package/dist/web/generate-helpers.js.map +1 -1
- package/dist/web/generate-helpers.test-d.js +7 -5
- package/dist/web/generate-helpers.test-d.js.map +1 -1
- package/dist/web/generate-helpers.test.js +2 -2
- package/dist/web/generate-helpers.test.js.map +1 -1
- package/dist/web/hooks/index.d.ts +3 -1
- package/dist/web/hooks/index.js +3 -1
- package/dist/web/hooks/index.js.map +1 -1
- package/dist/web/hooks/test/utils.d.ts +2 -1
- package/dist/web/hooks/test/utils.js +1 -1
- package/dist/web/hooks/test/utils.js.map +1 -1
- package/dist/web/hooks/use-call-tool.test.js +39 -25
- package/dist/web/hooks/use-call-tool.test.js.map +1 -1
- package/dist/web/hooks/use-display-mode.d.ts +1 -1
- package/dist/web/hooks/use-display-mode.js +1 -1
- package/dist/web/hooks/use-display-mode.js.map +1 -1
- package/dist/web/hooks/use-display-mode.test.js +57 -21
- package/dist/web/hooks/use-display-mode.test.js.map +1 -1
- package/dist/web/hooks/use-download.d.ts +1 -3
- package/dist/web/hooks/use-download.js +1 -2
- package/dist/web/hooks/use-download.js.map +1 -1
- package/dist/web/hooks/use-download.test.js +18 -10
- package/dist/web/hooks/use-download.test.js.map +1 -1
- package/dist/web/hooks/use-files.test.js +11 -3
- package/dist/web/hooks/use-files.test.js.map +1 -1
- package/dist/web/hooks/use-host.d.ts +22 -0
- package/dist/web/hooks/use-host.js +30 -0
- package/dist/web/hooks/use-host.js.map +1 -0
- package/dist/web/hooks/use-host.test.d.ts +1 -0
- package/dist/web/hooks/use-host.test.js +59 -0
- package/dist/web/hooks/use-host.test.js.map +1 -0
- package/dist/web/hooks/use-open-external.test.js +15 -5
- package/dist/web/hooks/use-open-external.test.js.map +1 -1
- package/dist/web/hooks/use-register-view-tool.d.ts +37 -0
- package/dist/web/hooks/use-register-view-tool.js +50 -0
- package/dist/web/hooks/use-register-view-tool.js.map +1 -0
- package/dist/web/hooks/use-request-close.test.js +35 -40
- package/dist/web/hooks/use-request-close.test.js.map +1 -1
- package/dist/web/hooks/use-request-modal.test.js +11 -1
- package/dist/web/hooks/use-request-modal.test.js.map +1 -1
- package/dist/web/hooks/use-request-size.d.ts +1 -1
- package/dist/web/hooks/use-request-size.js +1 -1
- package/dist/web/hooks/use-request-size.js.map +1 -1
- package/dist/web/hooks/use-request-size.test.js +35 -53
- package/dist/web/hooks/use-request-size.test.js.map +1 -1
- package/dist/web/hooks/use-set-open-in-app-url.test.js +26 -12
- package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -1
- package/dist/web/hooks/use-tool-info.d.ts +18 -19
- package/dist/web/hooks/use-tool-info.js +4 -8
- package/dist/web/hooks/use-tool-info.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test-d.js +11 -34
- package/dist/web/hooks/use-tool-info.test-d.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test.js +42 -37
- package/dist/web/hooks/use-tool-info.test.js.map +1 -1
- package/dist/web/hooks/use-user.d.ts +3 -2
- package/dist/web/hooks/use-user.js +4 -3
- package/dist/web/hooks/use-user.js.map +1 -1
- package/dist/web/hooks/use-user.test.js +85 -37
- package/dist/web/hooks/use-user.test.js.map +1 -1
- package/dist/web/hooks/use-view-state.js +19 -9
- package/dist/web/hooks/use-view-state.js.map +1 -1
- package/dist/web/hooks/use-view-state.test.js +39 -4
- package/dist/web/hooks/use-view-state.test.js.map +1 -1
- package/dist/web/hooks/use-viewport.d.ts +20 -0
- package/dist/web/hooks/use-viewport.js +21 -0
- package/dist/web/hooks/use-viewport.js.map +1 -0
- package/dist/web/hooks/use-viewport.test.d.ts +1 -0
- package/dist/web/hooks/use-viewport.test.js +109 -0
- package/dist/web/hooks/use-viewport.test.js.map +1 -0
- package/dist/web/mount-view.d.ts +3 -2
- package/dist/web/mount-view.js +5 -8
- package/dist/web/mount-view.js.map +1 -1
- package/package.json +33 -27
- package/dist/cli/resolve-views-dir.d.ts +0 -1
- package/dist/cli/resolve-views-dir.js.map +0 -1
- package/dist/web/bridges/apps-sdk/adaptor.d.ts +0 -28
- package/dist/web/bridges/apps-sdk/adaptor.js +0 -113
- package/dist/web/bridges/apps-sdk/adaptor.js.map +0 -1
- package/dist/web/bridges/mcp-app/adaptor.d.ts +0 -52
- package/dist/web/bridges/mcp-app/adaptor.js +0 -280
- package/dist/web/bridges/mcp-app/adaptor.js.map +0 -1
- package/dist/web/hooks/use-layout.d.ts +0 -24
- package/dist/web/hooks/use-layout.js +0 -25
- package/dist/web/hooks/use-layout.js.map +0 -1
- package/dist/web/hooks/use-layout.test.js +0 -96
- package/dist/web/hooks/use-layout.test.js.map +0 -1
- package/dist/web/plugin/data-llm.test.js +0 -81
- package/dist/web/plugin/data-llm.test.js.map +0 -1
- package/dist/web/plugin/plugin.d.ts +0 -33
- package/dist/web/plugin/plugin.js +0 -189
- package/dist/web/plugin/plugin.js.map +0 -1
- package/dist/web/plugin/scan-views.js.map +0 -1
- package/dist/web/plugin/scan-views.test.js.map +0 -1
- package/dist/web/plugin/transform-data-llm.d.ts +0 -12
- package/dist/web/plugin/transform-data-llm.js +0 -96
- package/dist/web/plugin/transform-data-llm.js.map +0 -1
- package/dist/web/plugin/transform-data-llm.test.js +0 -81
- package/dist/web/plugin/transform-data-llm.test.js.map +0 -1
- package/dist/web/plugin/validate-view.d.ts +0 -1
- package/dist/web/plugin/validate-view.js.map +0 -1
- package/dist/web/plugin/validate-view.test.js.map +0 -1
- /package/dist/{web/hooks/use-layout.test.d.ts → cli/build-steps.test.d.ts} +0 -0
- /package/dist/{web/plugin/data-llm.test.d.ts → context-warnings.test.d.ts} +0 -0
- /package/dist/{web/plugin/transform-data-llm.test.d.ts → server/auth/discovery.test.d.ts} +0 -0
- /package/dist/{web/plugin/validate-view.test.d.ts → server/auth/providers/auth0.test.d.ts} +0 -0
- /package/dist/{web/plugin/scan-views.test.d.ts → server/auth-extra.test-d.d.ts} +0 -0
- /package/dist/{web/plugin → views}/scan-views.d.ts +0 -0
- /package/dist/{web/plugin → views}/scan-views.test.js +0 -0
- /package/dist/{web/plugin → views}/validate-view.test.js +0 -0
package/dist/server/express.js
CHANGED
|
@@ -1,7 +1,36 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import {
|
|
2
|
+
import { toNodeHandler } from "@modelcontextprotocol/node";
|
|
3
|
+
import { createMcpHandler, UnsupportedProtocolVersionError, } from "@modelcontextprotocol/server";
|
|
3
4
|
import cors from "cors";
|
|
4
5
|
import express from "express";
|
|
6
|
+
/**
|
|
7
|
+
* The `/mcp` fetch handler for a {@link Skybridge} app: a `Request` →
|
|
8
|
+
* `Response` function that builds a fresh MCP server per request.
|
|
9
|
+
*
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
export function buildMcpHandler(skybridgeApp) {
|
|
13
|
+
return createMcpHandler(() => skybridgeApp.createServerInstance(), {
|
|
14
|
+
onerror: (error) => {
|
|
15
|
+
if (error instanceof UnsupportedProtocolVersionError) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
console.error("Error handling MCP request:", error);
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Build the bare Express app a {@link Skybridge} instance owns: the instance
|
|
24
|
+
* plus the built-in `express.json()` body parser, tuned by the `json` config
|
|
25
|
+
* field.
|
|
26
|
+
*
|
|
27
|
+
* @internal
|
|
28
|
+
*/
|
|
29
|
+
export function createBaseApp(json) {
|
|
30
|
+
const app = express();
|
|
31
|
+
app.use(express.json(json));
|
|
32
|
+
return app;
|
|
33
|
+
}
|
|
5
34
|
function parseControlPort(raw) {
|
|
6
35
|
if (raw === undefined) {
|
|
7
36
|
return null;
|
|
@@ -32,8 +61,8 @@ function defaultErrorHandler(err, _req, res, _next) {
|
|
|
32
61
|
});
|
|
33
62
|
}
|
|
34
63
|
}
|
|
35
|
-
export async function createApp({
|
|
36
|
-
const app =
|
|
64
|
+
export async function createApp({ app: skybridgeApp, httpServer, mcpHandler, errorMiddleware = [], }) {
|
|
65
|
+
const app = skybridgeApp.express;
|
|
37
66
|
// Read `process.env.NODE_ENV` inline: wrangler/esbuild only substitute the literal expression,
|
|
38
67
|
// so a local const would defeat dead-code elimination of the dev-only imports below.
|
|
39
68
|
if (process.env.NODE_ENV !== "production") {
|
|
@@ -55,43 +84,19 @@ export async function createApp({ mcpServer, httpServer, errorMiddleware = [], }
|
|
|
55
84
|
app.use("/assets", cors());
|
|
56
85
|
app.use("/assets", express.static(assetsPath));
|
|
57
86
|
}
|
|
58
|
-
app.use("/mcp", mcpMiddleware(
|
|
87
|
+
app.use("/mcp", mcpMiddleware(mcpHandler ?? buildMcpHandler(skybridgeApp)));
|
|
59
88
|
applyMiddlewares(app, errorMiddleware);
|
|
60
89
|
app.use("/mcp", defaultErrorHandler);
|
|
61
90
|
return app;
|
|
62
91
|
}
|
|
63
|
-
const mcpMiddleware = (
|
|
92
|
+
const mcpMiddleware = (mcpHandler) => {
|
|
93
|
+
const handler = toNodeHandler(mcpHandler);
|
|
64
94
|
return async (req, res, next) => {
|
|
65
|
-
if (req.method !== "POST") {
|
|
66
|
-
res.writeHead(405).end(JSON.stringify({
|
|
67
|
-
jsonrpc: "2.0",
|
|
68
|
-
error: {
|
|
69
|
-
code: -32000,
|
|
70
|
-
message: "Method not allowed.",
|
|
71
|
-
},
|
|
72
|
-
id: null,
|
|
73
|
-
}));
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
95
|
try {
|
|
77
|
-
const transport = new StreamableHTTPServerTransport({
|
|
78
|
-
sessionIdGenerator: undefined,
|
|
79
|
-
// Respond with a single JSON body instead of SSE. Skybridge's stateless
|
|
80
|
-
// transport never streams server-initiated messages, so SSE adds no
|
|
81
|
-
// capability — and on workerd specifically, `cloudflare:node`'s http
|
|
82
|
-
// bridge silently drops chunked writes that happen after the request
|
|
83
|
-
// handler awaits, which manifests as a 200 with empty body for any
|
|
84
|
-
// async tools/call.
|
|
85
|
-
enableJsonResponse: true,
|
|
86
|
-
});
|
|
87
|
-
res.on("close", () => {
|
|
88
|
-
transport.close();
|
|
89
|
-
});
|
|
90
|
-
await server.connectStatelessTransport(transport);
|
|
91
96
|
// Express strips the mount path from req.url (e.g. "/mcp" becomes "/").
|
|
92
|
-
// Restore it so the SDK builds the correct
|
|
97
|
+
// Restore it so the SDK builds the correct request URL.
|
|
93
98
|
req.url = req.originalUrl;
|
|
94
|
-
await
|
|
99
|
+
await handler(req, res, req.body);
|
|
95
100
|
}
|
|
96
101
|
catch (error) {
|
|
97
102
|
next(error);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"express.js","sourceRoot":"","sources":["../../src/server/express.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"express.js","sourceRoot":"","sources":["../../src/server/express.ts"],"names":[],"mappings":"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","sourcesContent":["import type http from \"node:http\";\nimport path from \"node:path\";\nimport { toNodeHandler } from \"@modelcontextprotocol/node\";\nimport {\n createMcpHandler,\n type McpHttpHandler,\n UnsupportedProtocolVersionError,\n} from \"@modelcontextprotocol/server\";\nimport cors from \"cors\";\nimport express from \"express\";\nimport type { Skybridge } from \"./app.js\";\nimport type { JsonOptions } from \"./server.js\";\n\ntype SkybridgeApp = Pick<Skybridge, \"express\" | \"createServerInstance\">;\n\n/**\n * The `/mcp` fetch handler for a {@link Skybridge} app: a `Request` →\n * `Response` function that builds a fresh MCP server per request.\n *\n * @internal\n */\nexport function buildMcpHandler(skybridgeApp: SkybridgeApp): McpHttpHandler {\n return createMcpHandler(() => skybridgeApp.createServerInstance(), {\n onerror: (error) => {\n if (error instanceof UnsupportedProtocolVersionError) {\n return;\n }\n console.error(\"Error handling MCP request:\", error);\n },\n });\n}\n\n/**\n * Build the bare Express app a {@link Skybridge} instance owns: the instance\n * plus the built-in `express.json()` body parser, tuned by the `json` config\n * field.\n *\n * @internal\n */\nexport function createBaseApp(json?: JsonOptions): express.Express {\n const app = express();\n app.use(express.json(json));\n return app;\n}\n\nfunction parseControlPort(raw: string | undefined): number | null {\n if (raw === undefined) {\n return null;\n }\n const n = Number.parseInt(raw, 10);\n if (!Number.isFinite(n) || n <= 0 || n >= 65536) {\n return null;\n }\n return n;\n}\n\nfunction applyMiddlewares(\n app: express.Express,\n middlewares: Array<{\n path?: string;\n handlers: express.ErrorRequestHandler[];\n }>,\n): void {\n for (const middleware of middlewares) {\n if (middleware.path) {\n app.use(middleware.path, ...middleware.handlers);\n } else {\n app.use(...middleware.handlers);\n }\n }\n}\n\nfunction defaultErrorHandler(\n err: unknown,\n _req: express.Request,\n res: express.Response,\n _next: express.NextFunction,\n) {\n console.error(\"Error handling MCP request:\", err);\n if (!res.headersSent) {\n res.status(500).json({\n jsonrpc: \"2.0\",\n error: { code: -32603, message: \"Internal server error\" },\n id: null,\n });\n }\n}\n\nexport async function createApp({\n app: skybridgeApp,\n httpServer,\n mcpHandler,\n errorMiddleware = [],\n}: {\n app: SkybridgeApp;\n httpServer: http.Server;\n mcpHandler?: McpHttpHandler;\n errorMiddleware?: {\n path?: string;\n handlers: express.ErrorRequestHandler[];\n }[];\n}): Promise<express.Express> {\n const app = skybridgeApp.express;\n\n // Read `process.env.NODE_ENV` inline: wrangler/esbuild only substitute the literal expression,\n // so a local const would defeat dead-code elimination of the dev-only imports below.\n if (process.env.NODE_ENV !== \"production\") {\n const { devtoolsStaticServer } = await import(\"@skybridge/devtools\");\n app.use(await devtoolsStaticServer());\n const { viewsDevServer } = await import(\"./viewsDevServer.js\");\n app.use(await viewsDevServer(httpServer));\n\n const controlPort = parseControlPort(process.env.__TUNNEL_CONTROL_PORT);\n if (controlPort !== null) {\n const { createTunnelProxyRouter } = await import(\n \"./tunnel-proxy-router.js\"\n );\n app.use(createTunnelProxyRouter(controlPort));\n } else if (process.env.__TUNNEL_CONTROL_PORT !== undefined) {\n console.warn(\n `Ignoring invalid __TUNNEL_CONTROL_PORT=${process.env.__TUNNEL_CONTROL_PORT}`,\n );\n }\n } else {\n const assetsPath = path.join(process.cwd(), \"dist\", \"assets\");\n\n app.use(\"/assets\", cors());\n app.use(\"/assets\", express.static(assetsPath));\n }\n\n app.use(\"/mcp\", mcpMiddleware(mcpHandler ?? buildMcpHandler(skybridgeApp)));\n\n applyMiddlewares(app, errorMiddleware);\n\n app.use(\"/mcp\", defaultErrorHandler);\n\n return app;\n}\n\nconst mcpMiddleware = (mcpHandler: McpHttpHandler): express.RequestHandler => {\n const handler = toNodeHandler(mcpHandler);\n\n return async (\n req: express.Request,\n res: express.Response,\n next: express.NextFunction,\n ) => {\n try {\n // Express strips the mount path from req.url (e.g. \"/mcp\" becomes \"/\").\n // Restore it so the SDK builds the correct request URL.\n req.url = req.originalUrl;\n await handler(req, res, req.body);\n } catch (error) {\n next(error);\n }\n };\n};\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import http from "node:http";
|
|
2
|
+
import { Client, StreamableHTTPClientTransport, } from "@modelcontextprotocol/client";
|
|
2
3
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
3
|
-
import {
|
|
4
|
+
import { Skybridge } from "./app.js";
|
|
4
5
|
vi.mock("@skybridge/devtools", () => ({
|
|
5
6
|
devtoolsStaticServer: () => ((_req, _res, next) => next()),
|
|
6
7
|
}));
|
|
@@ -25,22 +26,22 @@ async function postMcp(port) {
|
|
|
25
26
|
async function postApi(port) {
|
|
26
27
|
return fetch(`http://localhost:${port}/api/test`, { method: "POST" });
|
|
27
28
|
}
|
|
28
|
-
describe("
|
|
29
|
+
describe("Skybridge.express", () => {
|
|
29
30
|
it("exposes a ready Express app immediately after construction", () => {
|
|
30
|
-
const
|
|
31
|
-
expect(
|
|
32
|
-
expect(typeof
|
|
33
|
-
expect(typeof
|
|
31
|
+
const app = new Skybridge({ name: "t", version: "0.0.0" }, (server) => server);
|
|
32
|
+
expect(app.express).toBeDefined();
|
|
33
|
+
expect(typeof app.express.use).toBe("function");
|
|
34
|
+
expect(typeof app.express.get).toBe("function");
|
|
34
35
|
});
|
|
35
|
-
it("
|
|
36
|
+
it("app.express.get registers a route reachable alongside /mcp", async () => {
|
|
36
37
|
const { createApp } = await import("./express.js");
|
|
37
|
-
const
|
|
38
|
-
|
|
38
|
+
const app = new Skybridge({ name: "t", version: "0.0.0" }, (server) => server);
|
|
39
|
+
app.express.get("/health", (_req, res) => {
|
|
39
40
|
res.json({ status: "ok" });
|
|
40
41
|
});
|
|
41
42
|
const httpServer = http.createServer();
|
|
42
|
-
await createApp({
|
|
43
|
-
const { port, server: listening } = await listen(
|
|
43
|
+
await createApp({ app, httpServer });
|
|
44
|
+
const { port, server: listening } = await listen(app.express);
|
|
44
45
|
openServer = listening;
|
|
45
46
|
const health = await fetch(`http://localhost:${port}/health`);
|
|
46
47
|
expect(health.status).toBe(200);
|
|
@@ -49,12 +50,12 @@ describe("McpServer.express", () => {
|
|
|
49
50
|
const mcp = await postMcp(port);
|
|
50
51
|
expect(mcp.status).not.toBe(404);
|
|
51
52
|
});
|
|
52
|
-
it("
|
|
53
|
+
it("app.use and app.express.use produce the same registration order", async () => {
|
|
53
54
|
const { createApp } = await import("./express.js");
|
|
54
55
|
const callsA = [];
|
|
55
56
|
const callsB = [];
|
|
56
|
-
const
|
|
57
|
-
const sA =
|
|
57
|
+
const buildApp = () => new Skybridge({ name: "t", version: "0.0.0" }, (server) => server);
|
|
58
|
+
const sA = buildApp();
|
|
58
59
|
sA.use((_req, _res, next) => {
|
|
59
60
|
callsA.push("first");
|
|
60
61
|
next();
|
|
@@ -63,7 +64,7 @@ describe("McpServer.express", () => {
|
|
|
63
64
|
callsA.push("second");
|
|
64
65
|
next();
|
|
65
66
|
});
|
|
66
|
-
const sB =
|
|
67
|
+
const sB = buildApp();
|
|
67
68
|
sB.express.use((_req, _res, next) => {
|
|
68
69
|
callsB.push("first");
|
|
69
70
|
next();
|
|
@@ -75,7 +76,7 @@ describe("McpServer.express", () => {
|
|
|
75
76
|
for (const s of [sA, sB]) {
|
|
76
77
|
s.express.get("/probe", (_req, res) => res.json({ ok: true }));
|
|
77
78
|
const httpServer = http.createServer();
|
|
78
|
-
await createApp({
|
|
79
|
+
await createApp({ app: s, httpServer });
|
|
79
80
|
const { port, server: listening } = await listen(s.express);
|
|
80
81
|
openServer = listening;
|
|
81
82
|
await fetch(`http://localhost:${port}/probe`);
|
|
@@ -86,26 +87,28 @@ describe("McpServer.express", () => {
|
|
|
86
87
|
});
|
|
87
88
|
it("useOnError still wraps thrown /mcp errors after the route is mounted", async () => {
|
|
88
89
|
const { createApp } = await import("./express.js");
|
|
89
|
-
const
|
|
90
|
+
const app = new Skybridge({ name: "t", version: "0.0.0" }, (server) => server);
|
|
90
91
|
// Register the error handler BEFORE createApp — useOnError should still
|
|
91
92
|
// apply it after /mcp, so /mcp errors hit it.
|
|
92
93
|
const seen = [];
|
|
93
|
-
|
|
94
|
+
app.useOnError((_err, _req, res, _next) => {
|
|
94
95
|
seen.push("useOnError");
|
|
95
96
|
res.status(503).json({ from: "useOnError" });
|
|
96
97
|
});
|
|
97
98
|
// Force the /mcp handler to throw so the error pipeline runs.
|
|
98
|
-
|
|
99
|
+
app.use("/mcp", () => {
|
|
100
|
+
throw new Error("boom");
|
|
101
|
+
});
|
|
99
102
|
const httpServer = http.createServer();
|
|
100
103
|
await createApp({
|
|
101
|
-
|
|
104
|
+
app,
|
|
102
105
|
httpServer,
|
|
103
|
-
// Mirror what run() does: forward the
|
|
106
|
+
// Mirror what run() does: forward the app's useOnError handlers
|
|
104
107
|
// to createApp so they get applied after /mcp.
|
|
105
108
|
// biome-ignore lint/complexity/useLiteralKeys: test mirrors run() internals to verify useOnError ordering
|
|
106
|
-
errorMiddleware:
|
|
109
|
+
errorMiddleware: app["errorMiddleware"],
|
|
107
110
|
});
|
|
108
|
-
const { port, server: listening } = await listen(
|
|
111
|
+
const { port, server: listening } = await listen(app.express);
|
|
109
112
|
openServer = listening;
|
|
110
113
|
const res = await postMcp(port);
|
|
111
114
|
expect(seen).toEqual(["useOnError"]);
|
|
@@ -113,6 +116,37 @@ describe("McpServer.express", () => {
|
|
|
113
116
|
expect(await res.json()).toEqual({ from: "useOnError" });
|
|
114
117
|
});
|
|
115
118
|
});
|
|
119
|
+
describe("Skybridge JSON body parser options", () => {
|
|
120
|
+
const largeBody = JSON.stringify({ data: "x".repeat(200 * 1024) });
|
|
121
|
+
async function postEcho(port, body) {
|
|
122
|
+
return fetch(`http://localhost:${port}/echo`, {
|
|
123
|
+
method: "POST",
|
|
124
|
+
headers: { "Content-Type": "application/json" },
|
|
125
|
+
body,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
it("forwards json options to express.json", async () => {
|
|
129
|
+
const app = new Skybridge({ name: "t", version: "0.0.0", json: { limit: "10mb" } }, (server) => server);
|
|
130
|
+
app.express.post("/echo", (req, res) => {
|
|
131
|
+
res.json({ received: req.body.data.length });
|
|
132
|
+
});
|
|
133
|
+
const { port, server: listening } = await listen(app.express);
|
|
134
|
+
openServer = listening;
|
|
135
|
+
const res = await postEcho(port, largeBody);
|
|
136
|
+
expect(res.status).toBe(200);
|
|
137
|
+
expect(await res.json()).toEqual({ received: 200 * 1024 });
|
|
138
|
+
});
|
|
139
|
+
it("keeps the default 100kb limit when no options are passed", async () => {
|
|
140
|
+
const app = new Skybridge({ name: "t", version: "0.0.0" }, (server) => server);
|
|
141
|
+
app.express.post("/echo", (_req, res) => {
|
|
142
|
+
res.json({ ok: true });
|
|
143
|
+
});
|
|
144
|
+
const { port, server: listening } = await listen(app.express);
|
|
145
|
+
openServer = listening;
|
|
146
|
+
const res = await postEcho(port, largeBody);
|
|
147
|
+
expect(res.status).toBe(413);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
116
150
|
describe("createApp", () => {
|
|
117
151
|
it("runs global custom middleware before the /mcp handler", async () => {
|
|
118
152
|
const { createApp } = await import("./express.js");
|
|
@@ -121,11 +155,11 @@ describe("createApp", () => {
|
|
|
121
155
|
calls.push("custom");
|
|
122
156
|
next();
|
|
123
157
|
};
|
|
124
|
-
const
|
|
125
|
-
|
|
158
|
+
const app = new Skybridge({ name: "t", version: "0.0.0" }, (server) => server);
|
|
159
|
+
app.use(mw);
|
|
126
160
|
const httpServer = http.createServer();
|
|
127
|
-
const
|
|
128
|
-
const { port, server: httpListening } = await listen(
|
|
161
|
+
const expressApp = await createApp({ app, httpServer });
|
|
162
|
+
const { port, server: httpListening } = await listen(expressApp);
|
|
129
163
|
openServer = httpListening;
|
|
130
164
|
await postMcp(port);
|
|
131
165
|
expect(calls).toEqual(["custom"]);
|
|
@@ -137,11 +171,11 @@ describe("createApp", () => {
|
|
|
137
171
|
calls.push("auth");
|
|
138
172
|
next();
|
|
139
173
|
};
|
|
140
|
-
const
|
|
141
|
-
|
|
174
|
+
const app = new Skybridge({ name: "t", version: "0.0.0" }, (server) => server);
|
|
175
|
+
app.use("/mcp", mw);
|
|
142
176
|
const httpServer = http.createServer();
|
|
143
|
-
const
|
|
144
|
-
const { port, server: httpListening } = await listen(
|
|
177
|
+
const expressApp = await createApp({ app, httpServer });
|
|
178
|
+
const { port, server: httpListening } = await listen(expressApp);
|
|
145
179
|
openServer = httpListening;
|
|
146
180
|
await postMcp(port);
|
|
147
181
|
expect(calls).toEqual(["auth"]);
|
|
@@ -153,11 +187,11 @@ describe("createApp", () => {
|
|
|
153
187
|
calls.push("reject");
|
|
154
188
|
res.status(401).json({ error: "Unauthorized" });
|
|
155
189
|
};
|
|
156
|
-
const
|
|
157
|
-
|
|
190
|
+
const app = new Skybridge({ name: "t", version: "0.0.0" }, (server) => server);
|
|
191
|
+
app.use("/mcp", reject);
|
|
158
192
|
const httpServer = http.createServer();
|
|
159
|
-
const
|
|
160
|
-
const { port, server: httpListening } = await listen(
|
|
193
|
+
const expressApp = await createApp({ app, httpServer });
|
|
194
|
+
const { port, server: httpListening } = await listen(expressApp);
|
|
161
195
|
openServer = httpListening;
|
|
162
196
|
const res = await postMcp(port);
|
|
163
197
|
expect(calls).toEqual(["reject"]);
|
|
@@ -175,12 +209,12 @@ describe("createApp", () => {
|
|
|
175
209
|
calls.push("B");
|
|
176
210
|
next();
|
|
177
211
|
};
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
212
|
+
const app = new Skybridge({ name: "t", version: "0.0.0" }, (server) => server);
|
|
213
|
+
app.use(mwA);
|
|
214
|
+
app.use(mwB);
|
|
181
215
|
const httpServer = http.createServer();
|
|
182
|
-
const
|
|
183
|
-
const { port, server: httpListening } = await listen(
|
|
216
|
+
const expressApp = await createApp({ app, httpServer });
|
|
217
|
+
const { port, server: httpListening } = await listen(expressApp);
|
|
184
218
|
openServer = httpListening;
|
|
185
219
|
await postMcp(port);
|
|
186
220
|
expect(calls).toEqual(["A", "B"]);
|
|
@@ -192,11 +226,11 @@ describe("createApp", () => {
|
|
|
192
226
|
calls.push("api");
|
|
193
227
|
next();
|
|
194
228
|
};
|
|
195
|
-
const
|
|
196
|
-
|
|
229
|
+
const app = new Skybridge({ name: "t", version: "0.0.0" }, (server) => server);
|
|
230
|
+
app.use("/api", apiMw);
|
|
197
231
|
const httpServer = http.createServer();
|
|
198
|
-
const
|
|
199
|
-
const { port, server: httpListening } = await listen(
|
|
232
|
+
const expressApp = await createApp({ app, httpServer });
|
|
233
|
+
const { port, server: httpListening } = await listen(expressApp);
|
|
200
234
|
openServer = httpListening;
|
|
201
235
|
// Hit /mcp — the /api middleware should NOT fire
|
|
202
236
|
await postMcp(port);
|
|
@@ -209,11 +243,11 @@ describe("createApp", () => {
|
|
|
209
243
|
router.get("/health", (_req, res) => {
|
|
210
244
|
res.json({ status: "ok" });
|
|
211
245
|
});
|
|
212
|
-
const
|
|
213
|
-
|
|
246
|
+
const app = new Skybridge({ name: "t", version: "0.0.0" }, (server) => server);
|
|
247
|
+
app.use(router);
|
|
214
248
|
const httpServer = http.createServer();
|
|
215
|
-
const
|
|
216
|
-
const { port, server: httpListening } = await listen(
|
|
249
|
+
const expressApp = await createApp({ app, httpServer });
|
|
250
|
+
const { port, server: httpListening } = await listen(expressApp);
|
|
217
251
|
openServer = httpListening;
|
|
218
252
|
const res = await fetch(`http://localhost:${port}/health`);
|
|
219
253
|
expect(res.status).toBe(200);
|
|
@@ -226,11 +260,11 @@ describe("createApp", () => {
|
|
|
226
260
|
router.get("/data", (_req, res) => {
|
|
227
261
|
res.json({ value: 42 });
|
|
228
262
|
});
|
|
229
|
-
const
|
|
230
|
-
|
|
263
|
+
const app = new Skybridge({ name: "t", version: "0.0.0" }, (server) => server);
|
|
264
|
+
app.use("/api", router);
|
|
231
265
|
const httpServer = http.createServer();
|
|
232
|
-
const
|
|
233
|
-
const { port, server: httpListening } = await listen(
|
|
266
|
+
const expressApp = await createApp({ app, httpServer });
|
|
267
|
+
const { port, server: httpListening } = await listen(expressApp);
|
|
234
268
|
openServer = httpListening;
|
|
235
269
|
const res = await fetch(`http://localhost:${port}/api/data`);
|
|
236
270
|
expect(res.status).toBe(200);
|
|
@@ -241,11 +275,11 @@ describe("createApp", () => {
|
|
|
241
275
|
const throwing = () => {
|
|
242
276
|
throw new Error("boom");
|
|
243
277
|
};
|
|
244
|
-
const
|
|
245
|
-
|
|
278
|
+
const app = new Skybridge({ name: "t", version: "0.0.0" }, (server) => server);
|
|
279
|
+
app.use("/explode", throwing);
|
|
246
280
|
const httpServer = http.createServer();
|
|
247
|
-
const
|
|
248
|
-
const { port, server: httpListening } = await listen(
|
|
281
|
+
const expressApp = await createApp({ app, httpServer });
|
|
282
|
+
const { port, server: httpListening } = await listen(expressApp);
|
|
249
283
|
openServer = httpListening;
|
|
250
284
|
const res = await fetch(`http://localhost:${port}/explode`);
|
|
251
285
|
expect(res.status).toBe(500);
|
|
@@ -256,14 +290,15 @@ describe("createApp", () => {
|
|
|
256
290
|
it("returns 500 JSON-RPC error when the MCP handler throws and no error middleware is registered", async () => {
|
|
257
291
|
const { createApp } = await import("./express.js");
|
|
258
292
|
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => { });
|
|
259
|
-
const
|
|
260
|
-
// Force the express-level error path: make
|
|
261
|
-
//
|
|
262
|
-
|
|
263
|
-
|
|
293
|
+
const app = new Skybridge({ name: "t", version: "0.0.0" }, (server) => server);
|
|
294
|
+
// Force the express-level error path: make a /mcp middleware throw so the
|
|
295
|
+
// error pipeline runs and lands in the default /mcp error handler.
|
|
296
|
+
app.use("/mcp", () => {
|
|
297
|
+
throw new Error("boom");
|
|
298
|
+
});
|
|
264
299
|
const httpServer = http.createServer();
|
|
265
|
-
const
|
|
266
|
-
const { port, server: httpListening } = await listen(
|
|
300
|
+
const expressApp = await createApp({ app, httpServer });
|
|
301
|
+
const { port, server: httpListening } = await listen(expressApp);
|
|
267
302
|
openServer = httpListening;
|
|
268
303
|
const res = await postMcp(port);
|
|
269
304
|
expect(res.status).toBe(500);
|
|
@@ -282,15 +317,17 @@ describe("createApp", () => {
|
|
|
282
317
|
calls.push("error-handler");
|
|
283
318
|
res.status(503).json({ custom: true });
|
|
284
319
|
};
|
|
285
|
-
const
|
|
286
|
-
|
|
320
|
+
const app = new Skybridge({ name: "t", version: "0.0.0" }, (server) => server);
|
|
321
|
+
app.use("/mcp", () => {
|
|
322
|
+
throw new Error("boom");
|
|
323
|
+
});
|
|
287
324
|
const httpServer = http.createServer();
|
|
288
|
-
const
|
|
289
|
-
|
|
325
|
+
const expressApp = await createApp({
|
|
326
|
+
app,
|
|
290
327
|
httpServer,
|
|
291
328
|
errorMiddleware: [{ handlers: [errorHandler] }],
|
|
292
329
|
});
|
|
293
|
-
const { port, server: httpListening } = await listen(
|
|
330
|
+
const { port, server: httpListening } = await listen(expressApp);
|
|
294
331
|
openServer = httpListening;
|
|
295
332
|
const res = await postMcp(port);
|
|
296
333
|
expect(calls).toEqual(["error-handler"]);
|
|
@@ -307,16 +344,18 @@ describe("createApp", () => {
|
|
|
307
344
|
const throwingApiRoute = (_req, _res, next) => {
|
|
308
345
|
next(new Error("api error"));
|
|
309
346
|
};
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
347
|
+
const app = new Skybridge({ name: "t", version: "0.0.0" }, (server) => server);
|
|
348
|
+
app.use("/mcp", () => {
|
|
349
|
+
throw new Error("boom");
|
|
350
|
+
});
|
|
351
|
+
app.use("/api/test", throwingApiRoute);
|
|
313
352
|
const httpServer = http.createServer();
|
|
314
|
-
const
|
|
315
|
-
|
|
353
|
+
const expressApp = await createApp({
|
|
354
|
+
app,
|
|
316
355
|
httpServer,
|
|
317
356
|
errorMiddleware: [{ path: "/mcp", handlers: [mcpErrorHandler] }],
|
|
318
357
|
});
|
|
319
|
-
const { port, server: httpListening } = await listen(
|
|
358
|
+
const { port, server: httpListening } = await listen(expressApp);
|
|
320
359
|
openServer = httpListening;
|
|
321
360
|
const mcpRes = await postMcp(port);
|
|
322
361
|
expect(calls).toEqual(["mcp-error-handler"]);
|
|
@@ -330,20 +369,16 @@ describe("createApp", () => {
|
|
|
330
369
|
});
|
|
331
370
|
it("handles concurrent /mcp requests without 'Already connected to a transport'", async () => {
|
|
332
371
|
const { createApp } = await import("./express.js");
|
|
333
|
-
const mcpServer = new McpServer({
|
|
334
|
-
name: "concurrent-test",
|
|
335
|
-
version: "0.0.0",
|
|
336
|
-
});
|
|
337
372
|
// Slow tool: keeps the underlying transport bound long enough to overlap
|
|
338
373
|
// with concurrent requests, exposing the shared-McpServer race.
|
|
339
|
-
|
|
374
|
+
const app = new Skybridge({ name: "concurrent-test", version: "0.0.0" }, (server) => server.registerTool({ name: "slow", description: "slow" }, async () => {
|
|
340
375
|
await new Promise((r) => setTimeout(r, 50));
|
|
341
376
|
return { content: [{ type: "text", text: "done" }] };
|
|
342
|
-
});
|
|
377
|
+
}));
|
|
343
378
|
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => { });
|
|
344
379
|
const httpServer = http.createServer();
|
|
345
|
-
const
|
|
346
|
-
const { port, server } = await listen(
|
|
380
|
+
const expressApp = await createApp({ app, httpServer });
|
|
381
|
+
const { port, server } = await listen(expressApp);
|
|
347
382
|
openServer = server;
|
|
348
383
|
const callBody = (id) => JSON.stringify({
|
|
349
384
|
jsonrpc: "2.0",
|
|
@@ -366,19 +401,19 @@ describe("createApp", () => {
|
|
|
366
401
|
});
|
|
367
402
|
});
|
|
368
403
|
describe("createApp Vercel mode", () => {
|
|
369
|
-
it("
|
|
404
|
+
it("app.run() returns the Express app without binding a port when VERCEL=1", async () => {
|
|
370
405
|
const prevVercel = process.env.VERCEL;
|
|
371
406
|
const prevEnv = process.env.NODE_ENV;
|
|
372
407
|
process.env.VERCEL = "1";
|
|
373
408
|
process.env.NODE_ENV = "production";
|
|
374
409
|
try {
|
|
375
410
|
vi.resetModules();
|
|
376
|
-
const {
|
|
377
|
-
const
|
|
378
|
-
const result = await
|
|
411
|
+
const { Skybridge: Reloaded } = await import("./app.js");
|
|
412
|
+
const app = new Reloaded({ name: "t", version: "0.0.0" }, (server) => server);
|
|
413
|
+
const result = await app.run();
|
|
379
414
|
expect(typeof result).toBe("function");
|
|
380
|
-
expect(result).toBe(
|
|
381
|
-
const { port, server: listening } = await listen(
|
|
415
|
+
expect(result).toBe(app.express);
|
|
416
|
+
const { port, server: listening } = await listen(app.express);
|
|
382
417
|
openServer = listening;
|
|
383
418
|
const res = await postMcp(port);
|
|
384
419
|
expect(res.status).not.toBe(404);
|
|
@@ -413,10 +448,10 @@ describe("createApp tunnel routes", () => {
|
|
|
413
448
|
process.env.__TUNNEL_CONTROL_PORT = String(controlPort);
|
|
414
449
|
try {
|
|
415
450
|
const { createApp } = await import("./express.js");
|
|
416
|
-
const
|
|
451
|
+
const app = new Skybridge({ name: "t", version: "0.0.0" }, (server) => server);
|
|
417
452
|
const httpServer = http.createServer();
|
|
418
|
-
const
|
|
419
|
-
const { port, server } = await listen(
|
|
453
|
+
const expressApp = await createApp({ app, httpServer });
|
|
454
|
+
const { port, server } = await listen(expressApp);
|
|
420
455
|
openServer = server;
|
|
421
456
|
const res = await fetch(`http://localhost:${port}/__skybridge/tunnel`, {
|
|
422
457
|
method: "POST",
|
|
@@ -440,11 +475,11 @@ describe("createApp tunnel routes", () => {
|
|
|
440
475
|
try {
|
|
441
476
|
vi.resetModules();
|
|
442
477
|
const { createApp } = await import("./express.js");
|
|
443
|
-
const {
|
|
444
|
-
const
|
|
478
|
+
const { Skybridge: ReloadedSkybridge } = await import("./app.js");
|
|
479
|
+
const app = new ReloadedSkybridge({ name: "t", version: "0.0.0" }, (server) => server);
|
|
445
480
|
const httpServer = http.createServer();
|
|
446
|
-
const
|
|
447
|
-
const { port, server } = await listen(
|
|
481
|
+
const expressApp = await createApp({ app, httpServer });
|
|
482
|
+
const { port, server } = await listen(expressApp);
|
|
448
483
|
openServer = server;
|
|
449
484
|
const res = await fetch(`http://localhost:${port}/__skybridge/tunnel`, {
|
|
450
485
|
method: "POST",
|
|
@@ -457,4 +492,28 @@ describe("createApp tunnel routes", () => {
|
|
|
457
492
|
}
|
|
458
493
|
});
|
|
459
494
|
});
|
|
495
|
+
describe("createApp mount path", () => {
|
|
496
|
+
it("serves /mcp with the mount path intact on the request URL", async () => {
|
|
497
|
+
const { createApp } = await import("./express.js");
|
|
498
|
+
const app = new Skybridge({ name: "t", version: "0.0.0" }, (server) => server.registerTool({
|
|
499
|
+
name: "echo-url",
|
|
500
|
+
description: "Echoes the request URL.",
|
|
501
|
+
inputSchema: {},
|
|
502
|
+
}, (_args, extra) => ({
|
|
503
|
+
content: [{ type: "text", text: extra.http?.req?.url ?? "" }],
|
|
504
|
+
})));
|
|
505
|
+
const httpServer = http.createServer();
|
|
506
|
+
const expressApp = await createApp({ app, httpServer });
|
|
507
|
+
const { port, server } = await listen(expressApp);
|
|
508
|
+
openServer = server;
|
|
509
|
+
const client = new Client({ name: "test-client", version: "0.0.0" });
|
|
510
|
+
await client.connect(new StreamableHTTPClientTransport(new URL(`http://localhost:${port}/mcp`)));
|
|
511
|
+
const result = (await client.callTool({
|
|
512
|
+
name: "echo-url",
|
|
513
|
+
arguments: {},
|
|
514
|
+
}));
|
|
515
|
+
await client.close();
|
|
516
|
+
expect(new URL(result.content[0]?.text ?? "").pathname).toBe("/mcp");
|
|
517
|
+
});
|
|
518
|
+
});
|
|
460
519
|
//# sourceMappingURL=express.test.js.map
|