skybridge 0.0.0-dev.88e5e83 → 0.0.0-dev.88f1889
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 +17 -23
- package/dist/cli/build-helpers.d.ts +6 -2
- package/dist/cli/build-helpers.js +39 -14
- package/dist/cli/build-helpers.js.map +1 -1
- package/dist/cli/build-helpers.test.js +73 -5
- 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.js +0 -1
- 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 +90 -0
- package/dist/cli/run-plain.js.map +1 -0
- package/dist/cli/telemetry.js +2 -0
- package/dist/cli/telemetry.js.map +1 -1
- package/dist/cli/use-execute-steps.d.ts +1 -1
- package/dist/cli/use-nodemon.d.ts +25 -1
- package/dist/cli/use-nodemon.js +96 -63
- 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 -2
- package/dist/commands/build.js +2 -61
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/dev.d.ts +1 -0
- package/dist/commands/dev.js +38 -6
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/start.js +2 -2
- 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 +194 -0
- package/dist/server/app.js +266 -0
- package/dist/server/app.js.map +1 -0
- package/dist/server/app.test.js +97 -0
- package/dist/server/app.test.js.map +1 -0
- package/dist/server/auth/discovery.d.ts +2 -1
- package/dist/server/auth/discovery.js +2 -2
- package/dist/server/auth/discovery.js.map +1 -1
- package/dist/server/auth/index.d.ts +35 -5
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/auth/providers/auth0.d.ts +25 -3
- package/dist/server/auth/providers/auth0.js +13 -8
- package/dist/server/auth/providers/auth0.js.map +1 -1
- package/dist/server/auth/providers/auth0.test.js +5 -3
- package/dist/server/auth/providers/auth0.test.js.map +1 -1
- 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.js +143 -0
- package/dist/server/auth/providers/authplane.test.js.map +1 -0
- package/dist/server/auth/providers/clerk.d.ts +40 -3
- package/dist/server/auth/providers/clerk.js +4 -1
- package/dist/server/auth/providers/clerk.js.map +1 -1
- package/dist/server/auth/providers/clerk.test.js +5 -3
- package/dist/server/auth/providers/clerk.test.js.map +1 -1
- package/dist/server/auth/providers/custom.d.ts +24 -6
- package/dist/server/auth/providers/custom.js +33 -11
- package/dist/server/auth/providers/custom.js.map +1 -1
- package/dist/server/auth/providers/custom.test.js +71 -17
- package/dist/server/auth/providers/custom.test.js.map +1 -1
- package/dist/server/auth/providers/descope.d.ts +32 -3
- package/dist/server/auth/providers/descope.js +13 -2
- package/dist/server/auth/providers/descope.js.map +1 -1
- package/dist/server/auth/providers/descope.test.js +42 -17
- package/dist/server/auth/providers/descope.test.js.map +1 -1
- package/dist/server/auth/providers/stytch.d.ts +16 -3
- package/dist/server/auth/providers/stytch.js +4 -1
- package/dist/server/auth/providers/stytch.js.map +1 -1
- 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 +28 -5
- package/dist/server/auth/providers/workos.js +6 -2
- package/dist/server/auth/providers/workos.js.map +1 -1
- 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 +3 -1
- package/dist/server/auth/setup.js +66 -26
- package/dist/server/auth/setup.js.map +1 -1
- package/dist/server/auth/setup.test.js +349 -20
- package/dist/server/auth/setup.test.js.map +1 -1
- package/dist/server/auth/verify.d.ts +42 -6
- package/dist/server/auth/verify.js +27 -3
- package/dist/server/auth/verify.js.map +1 -1
- package/dist/server/auth/verify.test.js +65 -0
- package/dist/server/auth/verify.test.js.map +1 -1
- package/dist/server/auth-extra.test-d.js +130 -0
- package/dist/server/auth-extra.test-d.js.map +1 -0
- package/dist/server/auth.d.ts +43 -6
- package/dist/server/auth.js +4 -5
- package/dist/server/auth.js.map +1 -1
- package/dist/server/build-manifest.test.js +7 -6
- package/dist/server/build-manifest.test.js.map +1 -1
- package/dist/server/content-helpers.d.ts +1 -1
- package/dist/server/content-helpers.js.map +1 -1
- package/dist/server/events.d.ts +113 -0
- package/dist/server/events.js +318 -0
- package/dist/server/events.js.map +1 -0
- package/dist/server/events.test.d.ts +1 -0
- package/dist/server/events.test.js +172 -0
- package/dist/server/events.test.js.map +1 -0
- package/dist/server/express.d.ts +23 -3
- package/dist/server/express.js +43 -32
- package/dist/server/express.js.map +1 -1
- package/dist/server/express.test.js +221 -103
- package/dist/server/express.test.js.map +1 -1
- 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/form-input.d.ts +120 -0
- package/dist/server/form-input.js +89 -0
- package/dist/server/form-input.js.map +1 -0
- 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 +18 -9
- package/dist/server/index.js +11 -3
- 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 +41 -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 +242 -134
- 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 +230 -0
- package/dist/server/register-tool.test.js.map +1 -0
- package/dist/server/server.d.ts +382 -129
- package/dist/server/server.js +468 -277
- 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 +86 -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 +167 -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.js +81 -8
- package/dist/server/view-resource-resolution.test.js.map +1 -1
- 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 +220 -201
- package/dist/test/utils.js.map +1 -1
- package/dist/test/view.test.js +44 -40
- 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 +14 -3
- package/dist/web/bridges/adaptor.js +185 -18
- package/dist/web/bridges/adaptor.js.map +1 -1
- package/dist/web/bridges/adaptor.test.js +95 -11
- package/dist/web/bridges/adaptor.test.js.map +1 -1
- package/dist/web/bridges/apps-sdk/bridge.js +1 -1
- package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
- package/dist/web/bridges/apps-sdk/types.d.ts +2 -3
- package/dist/web/bridges/apps-sdk/types.js.map +1 -1
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +4 -4
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js +4 -4
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -1
- package/dist/web/bridges/get-adaptor.test.js +2 -2
- package/dist/web/bridges/get-adaptor.test.js.map +1 -1
- package/dist/web/bridges/mcp-app/bridge.d.ts +1 -1
- package/dist/web/bridges/mcp-app/bridge.js +15 -6
- package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
- package/dist/web/bridges/mcp-app/bridge.test.js +2 -2
- package/dist/web/bridges/mcp-app/bridge.test.js.map +1 -1
- package/dist/web/bridges/mcp-app/types.d.ts +19 -2
- package/dist/web/bridges/mcp-app/types.js.map +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +2 -2
- 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.js +1 -1
- package/dist/web/bridges/mcp-app/view-tools.test.js.map +1 -1
- package/dist/web/bridges/types.d.ts +74 -16
- package/dist/web/bridges/types.js +1 -1
- package/dist/web/bridges/types.js.map +1 -1
- package/dist/web/components/modal-provider.d.ts +1 -1
- package/dist/web/create-store.test.js +2 -2
- package/dist/web/create-store.test.js.map +1 -1
- package/dist/web/data-llm.d.ts +1 -1
- package/dist/web/data-llm.js +3 -2
- package/dist/web/data-llm.js.map +1 -1
- package/dist/web/data-llm.test.js +17 -2
- package/dist/web/data-llm.test.js.map +1 -1
- package/dist/web/generate-helpers.d.ts +23 -19
- package/dist/web/generate-helpers.js +21 -17
- package/dist/web/generate-helpers.js.map +1 -1
- package/dist/web/generate-helpers.test-d.js +3 -3
- 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 +6 -1
- package/dist/web/hooks/index.js +6 -1
- package/dist/web/hooks/index.js.map +1 -1
- package/dist/web/hooks/openai-extensions.test.d.ts +1 -0
- package/dist/web/hooks/openai-extensions.test.js +107 -0
- package/dist/web/hooks/openai-extensions.test.js.map +1 -0
- 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.d.ts +2 -2
- package/dist/web/hooks/use-call-tool.js +15 -10
- package/dist/web/hooks/use-call-tool.js.map +1 -1
- package/dist/web/hooks/use-call-tool.test.js +15 -4
- package/dist/web/hooks/use-call-tool.test.js.map +1 -1
- package/dist/web/hooks/use-deep-link.d.ts +22 -0
- package/dist/web/hooks/use-deep-link.js +34 -0
- package/dist/web/hooks/use-deep-link.js.map +1 -0
- 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 +1 -1
- 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 +7 -7
- package/dist/web/hooks/use-download.test.js.map +1 -1
- package/dist/web/hooks/use-file-resource.d.ts +37 -0
- package/dist/web/hooks/use-file-resource.js +98 -0
- package/dist/web/hooks/use-file-resource.js.map +1 -0
- package/dist/web/hooks/use-file-resource.test.d.ts +1 -0
- package/dist/web/hooks/use-file-resource.test.js +65 -0
- package/dist/web/hooks/use-file-resource.test.js.map +1 -0
- package/dist/web/hooks/use-files.test.js +1 -1
- package/dist/web/hooks/use-files.test.js.map +1 -1
- package/dist/web/hooks/use-host.d.ts +44 -0
- package/dist/web/hooks/use-host.js +45 -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-model-context.d.ts +44 -0
- package/dist/web/hooks/use-model-context.js +63 -0
- package/dist/web/hooks/use-model-context.js.map +1 -0
- package/dist/web/hooks/use-open-external.test.js +2 -2
- package/dist/web/hooks/use-open-external.test.js.map +1 -1
- package/dist/web/hooks/use-open-file.d.ts +20 -0
- package/dist/web/hooks/use-open-file.js +25 -0
- package/dist/web/hooks/use-open-file.js.map +1 -0
- package/dist/web/hooks/use-register-view-tool.d.ts +3 -4
- package/dist/web/hooks/use-register-view-tool.js +1 -1
- package/dist/web/hooks/use-register-view-tool.js.map +1 -1
- package/dist/web/hooks/use-request-close.test.js +1 -1
- package/dist/web/hooks/use-request-close.test.js.map +1 -1
- package/dist/web/hooks/use-request-modal.test.js +1 -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 +1 -1
- package/dist/web/hooks/use-request-size.test.js.map +1 -1
- package/dist/web/hooks/use-send-follow-up-message.d.ts +8 -2
- package/dist/web/hooks/use-send-follow-up-message.js +6 -0
- package/dist/web/hooks/use-send-follow-up-message.js.map +1 -1
- package/dist/web/hooks/use-set-open-in-app-url.test.js +2 -8
- package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -1
- package/dist/web/hooks/use-tool-info.d.ts +1 -20
- package/dist/web/hooks/use-tool-info.js +0 -1
- package/dist/web/hooks/use-tool-info.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test-d.js +0 -5
- package/dist/web/hooks/use-tool-info.test-d.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test.js +2 -7
- 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 +5 -3
- 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 +33 -2
- 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-layout.test.js → use-viewport.test.js} +12 -32
- 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 +34 -29
- package/dist/cli/resolve-views-dir.d.ts +0 -1
- package/dist/cli/resolve-views-dir.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.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/scan-views.test.d.ts → server/app.test.d.ts} +0 -0
- /package/dist/{web/plugin/validate-view.test.d.ts → server/auth/providers/authplane.test.d.ts} +0 -0
- /package/dist/{web/plugin/transform-data-llm.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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/server/events.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomBytes, timingSafeEqual } from \"node:crypto\";\nimport http from \"node:http\";\nimport https from \"node:https\";\nimport {\n INVALID_PARAMS,\n ProtocolError,\n type McpServer as SdkMcpServer,\n type ServerContext,\n type StandardSchemaV1,\n type StandardSchemaWithJSON,\n} from \"@modelcontextprotocol/server\";\nimport { globalHttpAgent, globalHttpsAgent } from \"request-filtering-agent\";\nimport { Webhook } from \"standardwebhooks\";\nimport { z } from \"zod/v4\";\n\nconst EVENTS_ERROR = {\n NotFound: -32011,\n Forbidden: -32012,\n Unsupported: -32014,\n CallbackEndpointError: -32015,\n} as const;\n\nconst MAX_PAYLOAD_BYTES = 256 * 1024;\nconst DELIVERY_TIMEOUT_MS = 10_000;\nconst VERIFICATION_CACHE_MS = 60 * 60_000;\nconst MIN_TTL_MS = 60_000;\nconst DEFAULT_TTL_MS = 30 * 60_000;\nconst MAX_TTL_MS = 24 * 60 * 60_000;\n\n/**\n * A webhook subscription, as passed to `onSubscribe`. Whatever delivers the\n * events POSTs them to `url`, signed with `secret`, until `expiresAt`.\n */\nexport interface EventSubscription {\n /** Stable across refreshes: the same user subscribing to the same event with the same arguments and callback gets the same `id`. */\n id: string;\n /** Callback URL deliveries are POSTed to. */\n url: string;\n /** Standard Webhooks signing secret (`whsec_…`). A refresh may carry a new one. */\n secret: string;\n /** When the subscription lapses unless the host refreshes it. */\n expiresAt: Date;\n}\n\n/** An event occurrence, as passed to {@link deliverEvent}. */\nexport interface EventOccurrence {\n /** Event type name, as registered with `registerEvent`. */\n name: string;\n /** Stable identifier the host uses to drop duplicates, ideally the upstream's. Reuse it when retrying. */\n id: string;\n /** When the event happened. Defaults to now. */\n timestamp?: Date;\n /** Payload matching the event's `payloadSchema`. */\n data: unknown;\n}\n\n/** Outcome of {@link deliverEvent}. Retry with the same event `id` when `retryable`. */\nexport type DeliveryResult =\n | { ok: true }\n | { ok: false; reason: string; retryable: boolean; status?: number };\n\ntype EventSchema =\n | Record<string, StandardSchemaWithJSON>\n | StandardSchemaWithJSON;\n\nexport type InferEventSchema<T extends EventSchema> =\n T extends StandardSchemaWithJSON\n ? StandardSchemaV1.InferOutput<T>\n : T extends Record<string, StandardSchemaWithJSON>\n ? {\n [K in keyof T as undefined extends StandardSchemaV1.InferOutput<T[K]>\n ? never\n : K]: StandardSchemaV1.InferOutput<T[K]>;\n } & {\n [K in keyof T as undefined extends StandardSchemaV1.InferOutput<T[K]>\n ? K\n : never]?: StandardSchemaV1.InferOutput<T[K]>;\n }\n : never;\n\n/** The `registerEvent` config: how the event type is described in `events/list`. */\nexport interface EventConfig<TInput extends EventSchema> {\n /** Stable, specific name such as `comment.created`. */\n name: string;\n /** What the event is and when it fires. */\n description?: string;\n /** Subscription arguments (filters): a Zod shape or a Standard Schema object. */\n inputSchema?: TInput;\n /** Shape of the delivered `data`: a Zod shape or a Standard Schema object. */\n payloadSchema: EventSchema;\n _meta?: Record<string, unknown>;\n}\n\n/** What `registerEvent` does when the host subscribes and unsubscribes. */\nexport interface EventHooks<TArguments, TExtra> {\n /**\n * Runs on every `events/subscribe`, including the host's refreshes, once\n * the callback URL is verified. Hand the subscription to whatever delivers\n * the events, keyed by `subscription.id`. Throw (e.g. a `ProtocolError`) to\n * reject it, for instance when the user may not access what the arguments\n * point to.\n */\n onSubscribe(\n args: TArguments,\n context: { subscription: EventSubscription; extra: TExtra },\n ): void | Promise<void>;\n /** Runs on `events/unsubscribe`. Stop delivering to `subscription.id`. */\n onUnsubscribe(\n args: TArguments,\n context: { subscription: Pick<EventSubscription, \"id\">; extra: TExtra },\n ): void | Promise<void>;\n}\n\ntype RegisteredEvent = {\n name: string;\n description?: string;\n inputSchema: StandardSchemaWithJSON;\n payloadSchema: StandardSchemaWithJSON;\n _meta?: Record<string, unknown>;\n hooks: EventHooks<Record<string, unknown>, ServerContext>;\n};\n\nfunction toStandardSchema(schema: EventSchema): StandardSchemaWithJSON {\n return \"~standard\" in schema\n ? (schema as StandardSchemaWithJSON)\n : z.object(schema as unknown as z.ZodRawShape);\n}\n\nasync function validate(\n schema: StandardSchemaWithJSON,\n value: unknown,\n): Promise<Record<string, unknown>> {\n const result = await schema[\"~standard\"].validate(value);\n if (result.issues) {\n throw new ProtocolError(\n INVALID_PARAMS,\n `Invalid arguments: ${result.issues.map((issue) => issue.message).join(\", \")}`,\n );\n }\n return result.value as Record<string, unknown>;\n}\n\nfunction canonicalJson(value: unknown): string {\n if (Array.isArray(value)) {\n return `[${value.map(canonicalJson).join(\",\")}]`;\n }\n if (value && typeof value === \"object\") {\n return `{${Object.keys(value)\n .sort()\n .map(\n (key) =>\n `${JSON.stringify(key)}:${canonicalJson((value as Record<string, unknown>)[key])}`,\n )\n .join(\",\")}}`;\n }\n return JSON.stringify(value);\n}\n\nfunction subscriberOf(ctx: ServerContext): string {\n const authInfo = ctx.http?.authInfo;\n const subject = authInfo?.extra?.subject;\n const subscriber = typeof subject === \"string\" ? subject : authInfo?.clientId;\n if (!subscriber) {\n throw new ProtocolError(\n EVENTS_ERROR.Forbidden,\n \"Webhook subscriptions require an authenticated caller\",\n );\n }\n return subscriber;\n}\n\nfunction isValidSecret(secret: string): boolean {\n const encoded = secret.startsWith(\"whsec_\") ? secret.slice(6) : \"\";\n if (!/^[A-Za-z0-9+/]+={0,2}$/.test(encoded)) {\n return false;\n }\n const length = Buffer.from(encoded, \"base64\").length;\n return length >= 24 && length <= 64;\n}\n\nconst insecureCallbacksAllowed = () => process.env.NODE_ENV !== \"production\";\n\nfunction checkCallbackUrl(raw: string): string {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n throw new ProtocolError(INVALID_PARAMS, \"delivery.url is not a valid URL\");\n }\n const allowed =\n url.protocol === \"https:\" ||\n (url.protocol === \"http:\" && insecureCallbacksAllowed());\n if (!allowed) {\n throw new ProtocolError(INVALID_PARAMS, \"delivery.url must use https\");\n }\n return url.href;\n}\n\ntype PostOutcome =\n | { ok: true; body: string }\n | { ok: false; status?: number; reason: string };\n\nfunction post(\n url: string,\n headers: Record<string, string>,\n body: string,\n): Promise<PostOutcome> {\n const signal = AbortSignal.timeout(DELIVERY_TIMEOUT_MS);\n const failure = (error?: NodeJS.ErrnoException): PostOutcome => ({\n ok: false,\n reason: signal.aborted\n ? \"timeout\"\n : error?.code?.startsWith(\"ERR_TLS\") || error?.code?.includes(\"CERT\")\n ? \"tls_error\"\n : \"connection_refused\",\n });\n const [client, agent] = url.startsWith(\"https:\")\n ? [https, globalHttpsAgent]\n : [http, globalHttpAgent];\n return new Promise((resolve) => {\n const request = client.request(\n url,\n {\n method: \"POST\",\n headers: { ...headers, \"content-length\": Buffer.byteLength(body) },\n signal,\n ...(!insecureCallbacksAllowed() && { agent }),\n },\n (response) => {\n const status = response.statusCode ?? 0;\n const chunks: Buffer[] = [];\n let size = 0;\n response.on(\"data\", (chunk: Buffer) => {\n size += chunk.length;\n if (size <= 64 * 1024) {\n chunks.push(chunk);\n }\n });\n response.on(\"end\", () => {\n if (status >= 200 && status < 300) {\n resolve({ ok: true, body: Buffer.concat(chunks).toString() });\n } else {\n resolve({\n ok: false,\n status,\n reason: status >= 500 ? \"http_5xx\" : \"http_4xx\",\n });\n }\n });\n response.on(\"error\", (error) => resolve(failure(error)));\n response.on(\"close\", () => resolve(failure()));\n },\n );\n request.on(\"error\", (error) => resolve(failure(error)));\n request.end(body);\n });\n}\n\nfunction signedPost(\n subscription: { id: string; url: string; secret: string | string[] },\n messageId: string,\n body: string,\n): Promise<PostOutcome> {\n const signedAt = new Date();\n const secrets = [subscription.secret].flat();\n return post(\n subscription.url,\n {\n \"content-type\": \"application/json\",\n \"webhook-id\": messageId,\n \"webhook-timestamp\": String(Math.floor(signedAt.getTime() / 1000)),\n \"webhook-signature\": secrets\n .map((secret) => new Webhook(secret).sign(messageId, signedAt, body))\n .join(\" \"),\n \"x-mcp-subscription-id\": subscription.id,\n },\n body,\n );\n}\n\n/**\n * @experimental POST one event to a subscription handed out by `onSubscribe`,\n * as MCP Events expects: the event envelope, Standard Webhooks signature\n * headers, a 10 second deadline and, outside development, no requests to\n * private addresses. Pass the old and new secrets during a rotation so both\n * sign the delivery. A subscription past `expiresAt` is not posted to.\n * Retrying is up to the caller. API may change.\n *\n * @example\n * ```ts\n * const result = await deliverEvent(subscription, {\n * name: \"comment.created\",\n * id: comment.id,\n * data: { documentId: comment.documentId, excerpt: comment.text.slice(0, 280) },\n * });\n * if (!result.ok && result.retryable) {\n * await queue.retryLater(subscription, comment);\n * }\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/register-event\n */\nexport async function deliverEvent(\n subscription: Pick<EventSubscription, \"id\" | \"url\"> & {\n secret: string | string[];\n expiresAt: Date | string;\n },\n event: EventOccurrence,\n): Promise<DeliveryResult> {\n if (!(new Date(subscription.expiresAt).getTime() > Date.now())) {\n return { ok: false, reason: \"expired\", retryable: false };\n }\n const body = JSON.stringify({\n eventId: event.id,\n name: event.name,\n timestamp: (event.timestamp ?? new Date()).toISOString(),\n data: event.data,\n cursor: null,\n });\n if (Buffer.byteLength(body) > MAX_PAYLOAD_BYTES) {\n throw new Error(\n `Event \"${event.name}\" payload exceeds ${MAX_PAYLOAD_BYTES} bytes; send a summary and expose a tool to fetch the rest.`,\n );\n }\n const outcome = await signedPost(subscription, event.id, body);\n if (outcome.ok) {\n return { ok: true };\n }\n const status = outcome.status ?? 0;\n return {\n ...outcome,\n retryable:\n status !== 410 && status !== 413 && (status < 300 || status >= 400),\n };\n}\n\nconst verifiedCallbacks = new Map<string, number>();\n\nasync function verifyCallback(\n subscriber: string,\n subscription: EventSubscription,\n): Promise<void> {\n const key = `${subscriber}\\n${subscription.url}`;\n const now = Date.now();\n if ((verifiedCallbacks.get(key) ?? 0) > now) {\n return;\n }\n const challenge = randomBytes(24).toString(\"base64url\");\n const outcome = await signedPost(\n subscription,\n `msg_verification_${randomBytes(12).toString(\"hex\")}`,\n JSON.stringify({ type: \"verification\", challenge }),\n );\n let echoed: unknown;\n if (outcome.ok) {\n try {\n echoed = JSON.parse(outcome.body)?.challenge;\n } catch {}\n }\n const matched =\n typeof echoed === \"string\" &&\n echoed.length === challenge.length &&\n timingSafeEqual(Buffer.from(echoed), Buffer.from(challenge));\n if (!matched) {\n throw new ProtocolError(\n EVENTS_ERROR.CallbackEndpointError,\n \"Callback endpoint verification failed\",\n { reason: outcome.ok ? \"challenge_failed\" : outcome.reason },\n );\n }\n for (const [cached, until] of verifiedCallbacks) {\n if (until <= now) {\n verifiedCallbacks.delete(cached);\n }\n }\n verifiedCallbacks.set(key, now + VERIFICATION_CACHE_MS);\n}\n\nconst SubscriptionKeySchema = z.object({\n name: z.string(),\n arguments: z.record(z.string(), z.unknown()).optional(),\n delivery: z.object({ url: z.string() }),\n});\n\nconst SubscribeParamsSchema = SubscriptionKeySchema.extend({\n delivery: z.object({\n mode: z.string().optional(),\n url: z.string(),\n secret: z.string(),\n }),\n cursor: z.string().nullable().optional(),\n ttlMs: z.number().nullable().optional(),\n});\n\n/**\n * The event types registered on one server, and the `events/*` handlers that\n * serve them.\n *\n * @internal\n */\nexport class EventRegistry {\n private readonly events = new Map<string, RegisteredEvent>();\n\n constructor(\n private readonly server: Pick<SdkMcpServer[\"server\"], \"setRequestHandler\">,\n ) {}\n\n add(config: EventConfig<EventSchema>, hooks: EventHooks<never, never>): void {\n if (this.events.has(config.name)) {\n throw new Error(`Event \"${config.name}\" is already registered.`);\n }\n if (this.events.size === 0) {\n this.install();\n }\n this.events.set(config.name, {\n name: config.name,\n description: config.description,\n inputSchema: toStandardSchema(config.inputSchema ?? {}),\n payloadSchema: toStandardSchema(config.payloadSchema),\n _meta: config._meta,\n hooks,\n });\n }\n\n private install(): void {\n this.server.setRequestHandler(\n \"events/list\",\n { params: z.object({ cursor: z.string().optional() }).optional() },\n () => ({\n events: [...this.events.values()].map((event) => ({\n name: event.name,\n ...(event.description && { description: event.description }),\n delivery: [\"webhook\"],\n inputSchema: event.inputSchema[\"~standard\"].jsonSchema.input({\n target: \"draft-2020-12\",\n }),\n payloadSchema: event.payloadSchema[\"~standard\"].jsonSchema.output({\n target: \"draft-2020-12\",\n }),\n ...(event._meta && { _meta: event._meta }),\n })),\n }),\n );\n\n this.server.setRequestHandler(\n \"events/subscribe\",\n { params: SubscribeParamsSchema },\n async (params, ctx) => {\n if (params.delivery.mode && params.delivery.mode !== \"webhook\") {\n throw new ProtocolError(\n EVENTS_ERROR.Unsupported,\n `Unsupported delivery mode: ${params.delivery.mode}`,\n { feature: \"deliveryMode\", value: params.delivery.mode },\n );\n }\n if (!isValidSecret(params.delivery.secret)) {\n throw new ProtocolError(\n INVALID_PARAMS,\n \"delivery.secret must be whsec_ followed by base64 of 24 to 64 bytes\",\n );\n }\n const { event, subscriber, args, id, url } = await this.resolve(\n params,\n ctx,\n );\n const ttl =\n params.ttlMs === null ? MAX_TTL_MS : (params.ttlMs ?? DEFAULT_TTL_MS);\n const subscription: EventSubscription = {\n id,\n url,\n secret: params.delivery.secret,\n expiresAt: new Date(\n Date.now() + Math.min(Math.max(ttl, MIN_TTL_MS), MAX_TTL_MS),\n ),\n };\n await verifyCallback(subscriber, subscription);\n await event.hooks.onSubscribe(args, { subscription, extra: ctx });\n return {\n id,\n refreshBefore: subscription.expiresAt.toISOString(),\n cursor: null,\n truncated: false,\n };\n },\n );\n\n this.server.setRequestHandler(\n \"events/unsubscribe\",\n { params: SubscriptionKeySchema },\n async (params, ctx) => {\n const { event, args, id } = await this.resolve(params, ctx);\n await event.hooks.onUnsubscribe(args, {\n subscription: { id },\n extra: ctx,\n });\n return {};\n },\n );\n }\n\n private async resolve(\n params: z.infer<typeof SubscriptionKeySchema>,\n ctx: ServerContext,\n ) {\n const event = this.events.get(params.name);\n if (!event) {\n throw new ProtocolError(\n EVENTS_ERROR.NotFound,\n `Unknown event: ${params.name}`,\n { kind: \"event\" },\n );\n }\n const url = checkCallbackUrl(params.delivery.url);\n const subscriber = subscriberOf(ctx);\n const rawArguments = params.arguments ?? {};\n const args = await validate(event.inputSchema, rawArguments);\n const hash = createHash(\"sha256\")\n .update(canonicalJson([subscriber, url, event.name, rawArguments]))\n .digest(\"hex\");\n return { event, subscriber, args, url, id: `sub_${hash.slice(0, 32)}` };\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { createHmac, randomBytes } from "node:crypto";
|
|
2
|
+
import http from "node:http";
|
|
3
|
+
import net, {} from "node:net";
|
|
4
|
+
import { Client, InMemoryTransport } from "@modelcontextprotocol/client";
|
|
5
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { deliverEvent, Skybridge } from "./index.js";
|
|
8
|
+
async function startReceiver() {
|
|
9
|
+
const received = [];
|
|
10
|
+
const state = { dropResponses: false };
|
|
11
|
+
const server = http.createServer((req, res) => {
|
|
12
|
+
let body = "";
|
|
13
|
+
req.on("data", (chunk) => {
|
|
14
|
+
body += chunk;
|
|
15
|
+
});
|
|
16
|
+
req.on("end", () => {
|
|
17
|
+
received.push({ headers: req.headers, body });
|
|
18
|
+
if (state.dropResponses) {
|
|
19
|
+
res.writeHead(200, { "content-length": "1000" });
|
|
20
|
+
res.write("partial");
|
|
21
|
+
setTimeout(() => res.socket?.destroy(), 20);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const parsed = JSON.parse(body);
|
|
25
|
+
res.setHeader("content-type", "application/json");
|
|
26
|
+
res.end(parsed.type === "verification"
|
|
27
|
+
? JSON.stringify({ challenge: parsed.challenge })
|
|
28
|
+
: "{}");
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
|
|
32
|
+
const { port } = server.address();
|
|
33
|
+
return { url: `http://127.0.0.1:${port}/hooks`, received, server, state };
|
|
34
|
+
}
|
|
35
|
+
let cleanup;
|
|
36
|
+
afterEach(async () => {
|
|
37
|
+
await cleanup?.();
|
|
38
|
+
cleanup = undefined;
|
|
39
|
+
});
|
|
40
|
+
describe("MCP events", () => {
|
|
41
|
+
it("verifies the callback, hands the subscription to the hooks, and delivers signed events", async () => {
|
|
42
|
+
const receiver = await startReceiver();
|
|
43
|
+
const subscriptions = new Map();
|
|
44
|
+
const app = new Skybridge({
|
|
45
|
+
name: "t",
|
|
46
|
+
version: "0.0.1",
|
|
47
|
+
handler: (server) => server.registerEvent({
|
|
48
|
+
name: "comment.created",
|
|
49
|
+
inputSchema: { documentId: z.string() },
|
|
50
|
+
payloadSchema: { documentId: z.string(), excerpt: z.string() },
|
|
51
|
+
}, {
|
|
52
|
+
onSubscribe: (_args, { subscription }) => {
|
|
53
|
+
subscriptions.set(subscription.id, subscription);
|
|
54
|
+
},
|
|
55
|
+
onUnsubscribe: (_args, { subscription }) => {
|
|
56
|
+
subscriptions.delete(subscription.id);
|
|
57
|
+
},
|
|
58
|
+
}),
|
|
59
|
+
});
|
|
60
|
+
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
|
|
61
|
+
const send = clientTransport.send.bind(clientTransport);
|
|
62
|
+
clientTransport.send = (message) => send(message, {
|
|
63
|
+
authInfo: {
|
|
64
|
+
token: "t",
|
|
65
|
+
clientId: "chatgpt",
|
|
66
|
+
scopes: [],
|
|
67
|
+
extra: { subject: "user-1" },
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
const instance = await app.createServerInstance();
|
|
71
|
+
await instance.connect(serverTransport);
|
|
72
|
+
const client = new Client({ name: "c", version: "0.0.1" });
|
|
73
|
+
await client.connect(clientTransport);
|
|
74
|
+
cleanup = async () => {
|
|
75
|
+
await client.close();
|
|
76
|
+
await instance.close();
|
|
77
|
+
receiver.server.close();
|
|
78
|
+
};
|
|
79
|
+
expect(instance.getCapabilities().events).toEqual({});
|
|
80
|
+
const list = await client.request({ method: "events/list", params: {} }, z.object({
|
|
81
|
+
events: z.array(z.object({ name: z.string(), delivery: z.array(z.string()) })),
|
|
82
|
+
}));
|
|
83
|
+
expect(list.events).toEqual([
|
|
84
|
+
expect.objectContaining({
|
|
85
|
+
name: "comment.created",
|
|
86
|
+
delivery: ["webhook"],
|
|
87
|
+
}),
|
|
88
|
+
]);
|
|
89
|
+
const secret = `whsec_${randomBytes(32).toString("base64")}`;
|
|
90
|
+
const subscription = await client.request({
|
|
91
|
+
method: "events/subscribe",
|
|
92
|
+
params: {
|
|
93
|
+
name: "comment.created",
|
|
94
|
+
arguments: { documentId: "doc-1" },
|
|
95
|
+
delivery: { mode: "webhook", url: receiver.url, secret },
|
|
96
|
+
},
|
|
97
|
+
}, z.object({ id: z.string(), refreshBefore: z.string() }));
|
|
98
|
+
expect(receiver.received.map(({ body }) => JSON.parse(body).type)).toEqual([
|
|
99
|
+
"verification",
|
|
100
|
+
]);
|
|
101
|
+
const stored = subscriptions.get(subscription.id);
|
|
102
|
+
expect(stored).toMatchObject({ url: receiver.url, secret });
|
|
103
|
+
if (!stored) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
await expect(deliverEvent(stored, {
|
|
107
|
+
name: "comment.created",
|
|
108
|
+
id: "evt_1",
|
|
109
|
+
data: { documentId: "doc-1", excerpt: "hello" },
|
|
110
|
+
})).resolves.toEqual({ ok: true });
|
|
111
|
+
expect(receiver.received).toHaveLength(2);
|
|
112
|
+
const [, delivery] = receiver.received;
|
|
113
|
+
expect(JSON.parse(delivery?.body ?? "")).toMatchObject({
|
|
114
|
+
eventId: "evt_1",
|
|
115
|
+
name: "comment.created",
|
|
116
|
+
data: { documentId: "doc-1", excerpt: "hello" },
|
|
117
|
+
});
|
|
118
|
+
const headers = delivery?.headers ?? {};
|
|
119
|
+
expect(headers["x-mcp-subscription-id"]).toBe(subscription.id);
|
|
120
|
+
const expected = createHmac("sha256", Buffer.from(secret.slice(6), "base64"))
|
|
121
|
+
.update(`${headers["webhook-id"]}.${headers["webhook-timestamp"]}.${delivery?.body}`)
|
|
122
|
+
.digest("base64");
|
|
123
|
+
expect(headers["webhook-signature"]).toBe(`v1,${expected}`);
|
|
124
|
+
receiver.state.dropResponses = true;
|
|
125
|
+
const delivered = deliverEvent(stored, {
|
|
126
|
+
name: "comment.created",
|
|
127
|
+
id: "evt_2",
|
|
128
|
+
data: { documentId: "doc-1", excerpt: "dropped" },
|
|
129
|
+
});
|
|
130
|
+
await expect(Promise.race([
|
|
131
|
+
delivered,
|
|
132
|
+
new Promise((resolve) => setTimeout(() => resolve("hung"), 2_000)),
|
|
133
|
+
])).resolves.toMatchObject({ ok: false, retryable: true });
|
|
134
|
+
await client.request({
|
|
135
|
+
method: "events/unsubscribe",
|
|
136
|
+
params: {
|
|
137
|
+
name: "comment.created",
|
|
138
|
+
arguments: { documentId: "doc-1" },
|
|
139
|
+
delivery: { url: receiver.url },
|
|
140
|
+
},
|
|
141
|
+
}, z.object({}));
|
|
142
|
+
expect(subscriptions.size).toBe(0);
|
|
143
|
+
let connections = 0;
|
|
144
|
+
const privateTarget = net.createServer((socket) => {
|
|
145
|
+
connections += 1;
|
|
146
|
+
socket.destroy();
|
|
147
|
+
});
|
|
148
|
+
await new Promise((resolve) => privateTarget.listen(0, "127.0.0.1", resolve));
|
|
149
|
+
const { port: privatePort } = privateTarget.address();
|
|
150
|
+
const nodeEnv = process.env.NODE_ENV;
|
|
151
|
+
process.env.NODE_ENV = "production";
|
|
152
|
+
try {
|
|
153
|
+
await expect(client.request({
|
|
154
|
+
method: "events/subscribe",
|
|
155
|
+
params: {
|
|
156
|
+
name: "comment.created",
|
|
157
|
+
arguments: { documentId: "doc-1" },
|
|
158
|
+
delivery: {
|
|
159
|
+
url: `https://127.0.0.1:${privatePort}/hooks`,
|
|
160
|
+
secret,
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
}, z.object({}))).rejects.toMatchObject({ code: -32015 });
|
|
164
|
+
expect(connections).toBe(0);
|
|
165
|
+
}
|
|
166
|
+
finally {
|
|
167
|
+
process.env.NODE_ENV = nodeEnv;
|
|
168
|
+
privateTarget.close();
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
//# sourceMappingURL=events.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.test.js","sourceRoot":"","sources":["../../src/server/events.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHmac, randomBytes } from \"node:crypto\";\nimport http from \"node:http\";\nimport net, { type AddressInfo } from \"node:net\";\nimport { Client, InMemoryTransport } from \"@modelcontextprotocol/client\";\nimport type { JSONRPCMessage } from \"@modelcontextprotocol/server\";\nimport { afterEach, describe, expect, it } from \"vitest\";\nimport { z } from \"zod\";\nimport { deliverEvent, type EventSubscription, Skybridge } from \"./index.js\";\n\ntype Received = { headers: http.IncomingHttpHeaders; body: string };\n\nasync function startReceiver() {\n const received: Received[] = [];\n const state = { dropResponses: false };\n const server = http.createServer((req, res) => {\n let body = \"\";\n req.on(\"data\", (chunk) => {\n body += chunk;\n });\n req.on(\"end\", () => {\n received.push({ headers: req.headers, body });\n if (state.dropResponses) {\n res.writeHead(200, { \"content-length\": \"1000\" });\n res.write(\"partial\");\n setTimeout(() => res.socket?.destroy(), 20);\n return;\n }\n const parsed = JSON.parse(body);\n res.setHeader(\"content-type\", \"application/json\");\n res.end(\n parsed.type === \"verification\"\n ? JSON.stringify({ challenge: parsed.challenge })\n : \"{}\",\n );\n });\n });\n await new Promise<void>((resolve) => server.listen(0, \"127.0.0.1\", resolve));\n const { port } = server.address() as AddressInfo;\n return { url: `http://127.0.0.1:${port}/hooks`, received, server, state };\n}\n\nlet cleanup: (() => Promise<void>) | undefined;\nafterEach(async () => {\n await cleanup?.();\n cleanup = undefined;\n});\n\ndescribe(\"MCP events\", () => {\n it(\"verifies the callback, hands the subscription to the hooks, and delivers signed events\", async () => {\n const receiver = await startReceiver();\n const subscriptions = new Map<string, EventSubscription>();\n const app = new Skybridge({\n name: \"t\",\n version: \"0.0.1\",\n handler: (server) =>\n server.registerEvent(\n {\n name: \"comment.created\",\n inputSchema: { documentId: z.string() },\n payloadSchema: { documentId: z.string(), excerpt: z.string() },\n },\n {\n onSubscribe: (_args, { subscription }) => {\n subscriptions.set(subscription.id, subscription);\n },\n onUnsubscribe: (_args, { subscription }) => {\n subscriptions.delete(subscription.id);\n },\n },\n ),\n });\n\n const [clientTransport, serverTransport] =\n InMemoryTransport.createLinkedPair();\n const send = clientTransport.send.bind(clientTransport);\n clientTransport.send = (message: JSONRPCMessage) =>\n send(message, {\n authInfo: {\n token: \"t\",\n clientId: \"chatgpt\",\n scopes: [],\n extra: { subject: \"user-1\" },\n },\n });\n const instance = await app.createServerInstance();\n await instance.connect(serverTransport);\n const client = new Client({ name: \"c\", version: \"0.0.1\" });\n await client.connect(clientTransport);\n cleanup = async () => {\n await client.close();\n await instance.close();\n receiver.server.close();\n };\n\n expect(\n (instance.getCapabilities() as Record<string, unknown>).events,\n ).toEqual({});\n const list = await client.request(\n { method: \"events/list\", params: {} },\n z.object({\n events: z.array(\n z.object({ name: z.string(), delivery: z.array(z.string()) }),\n ),\n }),\n );\n expect(list.events).toEqual([\n expect.objectContaining({\n name: \"comment.created\",\n delivery: [\"webhook\"],\n }),\n ]);\n\n const secret = `whsec_${randomBytes(32).toString(\"base64\")}`;\n const subscription = await client.request(\n {\n method: \"events/subscribe\",\n params: {\n name: \"comment.created\",\n arguments: { documentId: \"doc-1\" },\n delivery: { mode: \"webhook\", url: receiver.url, secret },\n },\n },\n z.object({ id: z.string(), refreshBefore: z.string() }),\n );\n expect(receiver.received.map(({ body }) => JSON.parse(body).type)).toEqual([\n \"verification\",\n ]);\n\n const stored = subscriptions.get(subscription.id);\n expect(stored).toMatchObject({ url: receiver.url, secret });\n if (!stored) {\n return;\n }\n await expect(\n deliverEvent(stored, {\n name: \"comment.created\",\n id: \"evt_1\",\n data: { documentId: \"doc-1\", excerpt: \"hello\" },\n }),\n ).resolves.toEqual({ ok: true });\n\n expect(receiver.received).toHaveLength(2);\n const [, delivery] = receiver.received;\n expect(JSON.parse(delivery?.body ?? \"\")).toMatchObject({\n eventId: \"evt_1\",\n name: \"comment.created\",\n data: { documentId: \"doc-1\", excerpt: \"hello\" },\n });\n const headers: http.IncomingHttpHeaders = delivery?.headers ?? {};\n expect(headers[\"x-mcp-subscription-id\"]).toBe(subscription.id);\n const expected = createHmac(\n \"sha256\",\n Buffer.from(secret.slice(6), \"base64\"),\n )\n .update(\n `${headers[\"webhook-id\"]}.${headers[\"webhook-timestamp\"]}.${delivery?.body}`,\n )\n .digest(\"base64\");\n expect(headers[\"webhook-signature\"]).toBe(`v1,${expected}`);\n\n receiver.state.dropResponses = true;\n const delivered = deliverEvent(stored, {\n name: \"comment.created\",\n id: \"evt_2\",\n data: { documentId: \"doc-1\", excerpt: \"dropped\" },\n });\n await expect(\n Promise.race([\n delivered,\n new Promise((resolve) => setTimeout(() => resolve(\"hung\"), 2_000)),\n ]),\n ).resolves.toMatchObject({ ok: false, retryable: true });\n\n await client.request(\n {\n method: \"events/unsubscribe\",\n params: {\n name: \"comment.created\",\n arguments: { documentId: \"doc-1\" },\n delivery: { url: receiver.url },\n },\n },\n z.object({}),\n );\n expect(subscriptions.size).toBe(0);\n\n let connections = 0;\n const privateTarget = net.createServer((socket) => {\n connections += 1;\n socket.destroy();\n });\n await new Promise<void>((resolve) =>\n privateTarget.listen(0, \"127.0.0.1\", resolve),\n );\n const { port: privatePort } = privateTarget.address() as AddressInfo;\n const nodeEnv = process.env.NODE_ENV;\n process.env.NODE_ENV = \"production\";\n try {\n await expect(\n client.request(\n {\n method: \"events/subscribe\",\n params: {\n name: \"comment.created\",\n arguments: { documentId: \"doc-1\" },\n delivery: {\n url: `https://127.0.0.1:${privatePort}/hooks`,\n secret,\n },\n },\n },\n z.object({}),\n ),\n ).rejects.toMatchObject({ code: -32015 });\n expect(connections).toBe(0);\n } finally {\n process.env.NODE_ENV = nodeEnv;\n privateTarget.close();\n }\n });\n});\n"]}
|
package/dist/server/express.d.ts
CHANGED
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
import type http from "node:http";
|
|
2
|
+
import { type McpHttpHandler } from "@modelcontextprotocol/server";
|
|
2
3
|
import express from "express";
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
import type { Skybridge } from "./app.js";
|
|
5
|
+
import type { JsonOptions } from "./server.js";
|
|
6
|
+
type SkybridgeApp = Pick<Skybridge, "express" | "createServerInstance" | "ready">;
|
|
7
|
+
/**
|
|
8
|
+
* The `/mcp` fetch handler for a {@link Skybridge} app: a `Request` →
|
|
9
|
+
* `Response` function that builds a fresh MCP server per request.
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildMcpHandler(skybridgeApp: SkybridgeApp): McpHttpHandler;
|
|
14
|
+
/**
|
|
15
|
+
* Build the bare Express app a {@link Skybridge} instance owns: the instance
|
|
16
|
+
* plus the built-in `express.json()` body parser, tuned by the `json` config
|
|
17
|
+
* field.
|
|
18
|
+
*
|
|
19
|
+
* @internal
|
|
20
|
+
*/
|
|
21
|
+
export declare function createBaseApp(json?: JsonOptions): express.Express;
|
|
22
|
+
export declare function createApp({ app: skybridgeApp, httpServer, mcpHandler, errorMiddleware, }: {
|
|
23
|
+
app: SkybridgeApp;
|
|
6
24
|
httpServer: http.Server;
|
|
25
|
+
mcpHandler?: McpHttpHandler;
|
|
7
26
|
errorMiddleware?: {
|
|
8
27
|
path?: string;
|
|
9
28
|
handlers: express.ErrorRequestHandler[];
|
|
10
29
|
}[];
|
|
11
30
|
}): Promise<express.Express>;
|
|
31
|
+
export {};
|
package/dist/server/express.js
CHANGED
|
@@ -1,7 +1,41 @@
|
|
|
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.disable("x-powered-by");
|
|
32
|
+
app.use((_req, res, next) => {
|
|
33
|
+
res.setHeader("X-Powered-By", "skybridge");
|
|
34
|
+
next();
|
|
35
|
+
});
|
|
36
|
+
app.use(express.json(json));
|
|
37
|
+
return app;
|
|
38
|
+
}
|
|
5
39
|
function parseControlPort(raw) {
|
|
6
40
|
if (raw === undefined) {
|
|
7
41
|
return null;
|
|
@@ -32,8 +66,9 @@ function defaultErrorHandler(err, _req, res, _next) {
|
|
|
32
66
|
});
|
|
33
67
|
}
|
|
34
68
|
}
|
|
35
|
-
export async function createApp({
|
|
36
|
-
|
|
69
|
+
export async function createApp({ app: skybridgeApp, httpServer, mcpHandler, errorMiddleware = [], }) {
|
|
70
|
+
await skybridgeApp.ready();
|
|
71
|
+
const app = skybridgeApp.express;
|
|
37
72
|
// Read `process.env.NODE_ENV` inline: wrangler/esbuild only substitute the literal expression,
|
|
38
73
|
// so a local const would defeat dead-code elimination of the dev-only imports below.
|
|
39
74
|
if (process.env.NODE_ENV !== "production") {
|
|
@@ -55,43 +90,19 @@ export async function createApp({ mcpServer, httpServer, errorMiddleware = [], }
|
|
|
55
90
|
app.use("/assets", cors());
|
|
56
91
|
app.use("/assets", express.static(assetsPath));
|
|
57
92
|
}
|
|
58
|
-
app.use("/mcp", mcpMiddleware(
|
|
93
|
+
app.use("/mcp", mcpMiddleware(mcpHandler ?? buildMcpHandler(skybridgeApp)));
|
|
59
94
|
applyMiddlewares(app, errorMiddleware);
|
|
60
95
|
app.use("/mcp", defaultErrorHandler);
|
|
61
96
|
return app;
|
|
62
97
|
}
|
|
63
|
-
const mcpMiddleware = (
|
|
98
|
+
const mcpMiddleware = (mcpHandler) => {
|
|
99
|
+
const handler = toNodeHandler(mcpHandler);
|
|
64
100
|
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
101
|
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
102
|
// Express strips the mount path from req.url (e.g. "/mcp" becomes "/").
|
|
92
|
-
// Restore it so the SDK builds the correct
|
|
103
|
+
// Restore it so the SDK builds the correct request URL.
|
|
93
104
|
req.url = req.originalUrl;
|
|
94
|
-
await
|
|
105
|
+
await handler(req, res, req.body);
|
|
95
106
|
}
|
|
96
107
|
catch (error) {
|
|
97
108
|
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<\n Skybridge,\n \"express\" | \"createServerInstance\" | \"ready\"\n>;\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.disable(\"x-powered-by\");\n app.use((_req, res, next) => {\n res.setHeader(\"X-Powered-By\", \"skybridge\");\n next();\n });\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 await skybridgeApp.ready();\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"]}
|