akanjs 3.0.2 → 3.1.0-rc.0
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/CHANGELOG.md +26 -0
- package/base/baseEnv.ts +0 -4
- package/base/primitiveRegistry.ts +2 -2
- package/client/cookie.ts +5 -1
- package/client/csrLocation.ts +122 -0
- package/client/csrTypes.ts +32 -4
- package/client/device.ts +2 -1
- package/client/frameConfig.ts +78 -7
- package/client/index.ts +3 -0
- package/client/makePageProto.tsx +3 -2
- package/client/native/external-view.ts +1 -0
- package/client/route/RootLayoutDefinition.ts +7 -1
- package/client/route/RouteHead.ts +108 -0
- package/client/route/index.ts +1 -0
- package/client/router.ts +36 -23
- package/client/rscNavigation.ts +8 -2
- package/client/viewportMeta.ts +60 -0
- package/client/webManifest.ts +18 -0
- package/common/Logger.ts +7 -2
- package/common/PipelineNames.ts +31 -0
- package/common/PipelinePattern.ts +70 -0
- package/common/PipelinePlanSchema.ts +490 -0
- package/common/backoff.ts +14 -0
- package/common/index.ts +39 -0
- package/common/pipelinePlan.ts +259 -0
- package/common/routeConvention.ts +1 -0
- package/common/types.ts +17 -0
- package/constant/constantRegistry.ts +26 -6
- package/dictionary/base.dictionary.ts +8 -0
- package/dictionary/trans.ts +9 -1
- package/document/into.ts +5 -0
- package/fetch/client/fetchClient.ts +135 -31
- package/fetch/client/httpClient.ts +45 -0
- package/fetch/client/wsClient.ts +125 -16
- package/fetch/fetchType/buildFetch.type.ts +1 -0
- package/index.ts +243 -10
- package/package.json +1 -5
- package/server/akanApp.ts +5 -2
- package/server/akanServer.ts +44 -8
- package/server/artifact/builderRpc.ts +8 -3
- package/server/artifact/ipcTypes.ts +5 -2
- package/server/di/diLifecycle.ts +39 -3
- package/server/di/utils.ts +28 -1
- package/server/ed25519Key.ts +48 -0
- package/server/index.ts +1 -0
- package/server/machine/index.ts +2 -0
- package/server/machine/machineAccount.ts +27 -0
- package/server/machine/machineToken.ts +151 -0
- package/server/mcp/McpApps.ts +256 -0
- package/server/mcp/McpDispatcher.ts +31 -7
- package/server/mcp/McpRouter.ts +209 -46
- package/server/ops/opsToken.ts +3 -10
- package/server/resolver/database.resolver.ts +3 -0
- package/server/resolver/signal.resolver.ts +40 -3
- package/server/routeElementComposer.tsx +78 -2
- package/server/routeState.ts +6 -0
- package/server/routeTreeBuilder.ts +26 -9
- package/server/routing/apiRouter.ts +6 -3
- package/server/routing/roomReplay.ts +88 -0
- package/server/rscClient.tsx +53 -8
- package/server/rscWorker.tsx +141 -27
- package/server/rscWorkerHost.ts +18 -5
- package/server/sitemap.ts +14 -0
- package/server/ssrFromRscRenderer.tsx +12 -6
- package/server/ssrTypes.ts +3 -1
- package/server/systemPageDocument.tsx +38 -30
- package/server/types.tsx +2 -0
- package/server/webRouter.ts +74 -5
- package/service/injectInfo.ts +9 -1
- package/service/ipcTypes.ts +2 -1
- package/service/predefinedAdaptor/database.adaptor.ts +1 -146
- package/service/predefinedAdaptor/queue.adaptor.ts +44 -6
- package/service/predefinedAdaptor/queueBackoff.ts +12 -0
- package/service/predefinedAdaptor/solidQueue.adaptor.ts +17 -13
- package/service/predefinedAdaptor/sql/SqlDocumentStore.ts +44 -2
- package/service/predefinedAdaptor/sql/dialect/postgres.ts +3 -0
- package/service/predefinedAdaptor/sql/dialect/sqlite.ts +5 -2
- package/service/predefinedAdaptor/sql/search/fts5.ts +1 -1
- package/service/predefinedAdaptor/sql/types.ts +8 -6
- package/service/predefinedAdaptor/sql/values.ts +0 -23
- package/service/serve.ts +5 -1
- package/signal/agent.signal.ts +1 -1
- package/signal/base.signal.ts +2 -2
- package/signal/endpointIdempotency.ts +53 -0
- package/signal/internalArg.ts +14 -0
- package/signal/mcp/McpDocument.ts +38 -1
- package/signal/mcp/mcpProtocol.ts +18 -0
- package/signal/signalContext.ts +58 -30
- package/signal/signalRegistry.ts +14 -4
- package/signal/types.ts +18 -3
- package/store/agent/StoreSurfaceSource.ts +1 -1
- package/store/agentic/StToolBuilder.ts +3 -0
- package/test/conformance.ts +9 -33
- package/types/base/baseEnv.d.ts +0 -4
- package/types/base/primitiveRegistry.d.ts +1 -1
- package/types/client/csrLocation.d.ts +32 -0
- package/types/client/csrTypes.d.ts +40 -4
- package/types/client/frameConfig.d.ts +14 -2
- package/types/client/index.d.ts +3 -0
- package/types/client/native/external-view.d.ts +1 -0
- package/types/client/route/RootLayoutDefinition.d.ts +2 -1
- package/types/client/route/RouteHead.d.ts +13 -0
- package/types/client/route/index.d.ts +1 -0
- package/types/client/router.d.ts +6 -0
- package/types/client/rscNavigation.d.ts +4 -0
- package/types/client/viewportMeta.d.ts +18 -0
- package/types/client/webManifest.d.ts +4 -0
- package/types/common/PipelineNames.d.ts +6 -0
- package/types/common/PipelinePattern.d.ts +14 -0
- package/types/common/PipelinePlanSchema.d.ts +9 -0
- package/types/common/backoff.d.ts +10 -0
- package/types/common/index.d.ts +5 -0
- package/types/common/pipelinePlan.d.ts +224 -0
- package/types/common/types.d.ts +16 -0
- package/types/constant/constantRegistry.d.ts +5 -2
- package/types/dictionary/base.dictionary.d.ts +1 -1
- package/types/dictionary/dictionary.d.ts +8 -8
- package/types/document/into.d.ts +9 -0
- package/types/fetch/client/fetchClient.d.ts +31 -4
- package/types/fetch/client/httpClient.d.ts +8 -0
- package/types/fetch/client/wsClient.d.ts +26 -2
- package/types/fetch/fetchType/buildFetch.type.d.ts +1 -0
- package/types/index.d.ts +230 -9
- package/types/server/akanServer.d.ts +16 -1
- package/types/server/artifact/builderRpc.d.ts +7 -2
- package/types/server/artifact/ipcTypes.d.ts +5 -1
- package/types/server/di/utils.d.ts +1 -0
- package/types/server/ed25519Key.d.ts +13 -0
- package/types/server/index.d.ts +1 -0
- package/types/server/machine/index.d.ts +2 -0
- package/types/server/machine/machineAccount.d.ts +5 -0
- package/types/server/machine/machineToken.d.ts +29 -0
- package/types/server/machine.d.ts +1 -0
- package/types/server/mcp/McpApps.d.ts +50 -0
- package/types/server/mcp/McpDispatcher.d.ts +4 -1
- package/types/server/mcp/McpRouter.d.ts +4 -0
- package/types/server/resolver/signal.resolver.d.ts +2 -1
- package/types/server/routeElementComposer.d.ts +24 -4
- package/types/server/routeState.d.ts +4 -1
- package/types/server/routing/roomReplay.d.ts +24 -0
- package/types/server/rscWorkerHost.d.ts +5 -2
- package/types/server/sitemap.d.ts +2 -0
- package/types/server/ssrFromRscRenderer.d.ts +3 -3
- package/types/server/ssrTypes.d.ts +4 -1
- package/types/server/systemPageDocument.d.ts +2 -0
- package/types/server/types.d.ts +4 -1
- package/types/server/webRouter.d.ts +3 -1
- package/types/service/ipcTypes.d.ts +2 -0
- package/types/service/predefinedAdaptor/database.adaptor.d.ts +1 -24
- package/types/service/predefinedAdaptor/queue.adaptor.d.ts +10 -3
- package/types/service/predefinedAdaptor/queueBackoff.d.ts +4 -0
- package/types/service/predefinedAdaptor/solidQueue.adaptor.d.ts +3 -3
- package/types/service/predefinedAdaptor/sql/SqlDocumentStore.d.ts +6 -1
- package/types/service/predefinedAdaptor/sql/dialect/postgres.d.ts +1 -0
- package/types/service/predefinedAdaptor/sql/dialect/sqlite.d.ts +1 -0
- package/types/service/predefinedAdaptor/sql/search/fts5.d.ts +1 -1
- package/types/service/predefinedAdaptor/sql/types.d.ts +9 -5
- package/types/service/predefinedAdaptor/sql/values.d.ts +0 -3
- package/types/service/serve.d.ts +3 -0
- package/types/signal/endpointIdempotency.d.ts +7 -0
- package/types/signal/internalArg.d.ts +10 -0
- package/types/signal/mcp/McpDocument.d.ts +8 -0
- package/types/signal/mcp/mcpProtocol.d.ts +17 -0
- package/types/signal/signalContext.d.ts +5 -3
- package/types/signal/signalRegistry.d.ts +4 -4
- package/types/signal/types.d.ts +21 -1
- package/types/store/agentic/StToolBuilder.d.ts +2 -0
- package/types/test/conformance.d.ts +7 -10
- package/types/ui/Agent/Chat.d.ts +5 -1
- package/types/ui/Agent/EmbedApproval.d.ts +1 -0
- package/types/ui/Agent/index.d.ts +3 -0
- package/types/ui/Agent/useKeyboardFrame.d.ts +2 -0
- package/types/ui/Agent/useScreenActions.d.ts +18 -0
- package/types/ui/BottomSheet.d.ts +1 -0
- package/types/ui/Constant/Doc.d.ts +11 -17
- package/types/ui/Constant/index.d.ts +7 -2
- package/types/ui/Constant/schemaDoc.d.ts +11 -0
- package/types/ui/Dialog/Modal.d.ts +6 -1
- package/types/ui/Dialog/index.d.ts +1 -1
- package/types/ui/Dialog/modalSurface.d.ts +12 -0
- package/types/ui/Layout/BottomTab.d.ts +11 -2
- package/types/ui/Layout/FrameSlotFallback.d.ts +9 -0
- package/types/ui/Layout/Navbar.d.ts +4 -1
- package/types/ui/Layout/TopInset.d.ts +1 -1
- package/types/ui/Layout/index.d.ts +3 -3
- package/types/ui/Link/Action.d.ts +5 -1
- package/types/ui/Link/Anchor.d.ts +5 -3
- package/types/ui/Link/activePath.d.ts +13 -0
- package/types/ui/Link/index.d.ts +1 -1
- package/types/ui/Modal.d.ts +6 -1
- package/types/ui/Model/EditModal.d.ts +6 -1
- package/types/ui/Model/New.d.ts +6 -1
- package/types/ui/Model/ViewEditModal.d.ts +6 -1
- package/types/ui/Model/ViewModal.d.ts +6 -1
- package/types/ui/Model/newFormOrigin.d.ts +7 -0
- package/types/ui/Portal.d.ts +3 -1
- package/types/ui/Reference/origin.d.ts +12 -0
- package/types/ui/Signal/Doc.d.ts +23 -18
- package/types/ui/Signal/Listener.d.ts +2 -6
- package/types/ui/Signal/Object.d.ts +6 -10
- package/types/ui/Signal/Response.d.ts +3 -6
- package/types/ui/Signal/RestApi.d.ts +11 -15
- package/types/ui/Signal/WebSocket.d.ts +11 -21
- package/types/ui/Signal/endpointEntries.d.ts +8 -0
- package/types/ui/Signal/index.d.ts +35 -12
- package/types/ui/Signal/signalText.d.ts +178 -0
- package/types/ui/System/CSR.d.ts +2 -2
- package/types/ui/System/Client.d.ts +4 -3
- package/types/ui/System/Common.d.ts +27 -6
- package/types/ui/System/SSR.d.ts +6 -6
- package/types/ui/Tooltip.d.ts +4 -2
- package/types/ui/index.d.ts +2 -1
- package/types/ui/overlayLayer.d.ts +6 -0
- package/types/ui/overlayPosition.d.ts +17 -0
- package/types/vendor/@akanjs/native/packages/cli/src/api.d.ts +14 -3
- package/types/vendor/@akanjs/native/packages/cli/src/commands/update.d.ts +1 -1
- package/types/vendor/@akanjs/native/packages/cli/src/config.d.ts +30 -5
- package/types/vendor/@akanjs/native/packages/cli/src/lib/compat.d.ts +8 -0
- package/types/vendor/@akanjs/native/packages/cli/src/lib/desktop-server.d.ts +5 -0
- package/types/vendor/@akanjs/native/packages/cli/src/lib/distribution.d.ts +22 -0
- package/types/vendor/@akanjs/native/packages/cli/src/lib/project.d.ts +4 -0
- package/types/vendor/@akanjs/native/packages/cli/src/lib/publish.d.ts +16 -10
- package/types/vendor/@akanjs/native/packages/cli/src/lib/updates.d.ts +51 -7
- package/types/vendor/@akanjs/native/packages/cli/src/lib/vectors.d.ts +1 -1
- package/types/vendor/@akanjs/native/packages/cli/src/platforms/windows-installer.d.ts +7 -0
- package/types/vendor/@akanjs/native/packages/cli/src/platforms/windows.d.ts +5 -0
- package/types/vendor/@akanjs/native/packages/core/src/kernel.d.ts +36 -0
- package/types/vendor/@akanjs/native/packages/desktop/src/plugin.d.ts +2 -0
- package/types/vendor/@akanjs/native/packages/desktop/src/server.d.ts +11 -0
- package/types/vendor/@akanjs/native/plugins/external-view/src/desktop.d.ts +17 -0
- package/types/vendor/@akanjs/native/plugins/external-view/src/index.d.ts +132 -0
- package/types/vendor/@akanjs/native/plugins/external-view/src.d.ts +1 -0
- package/types/vendor/@akanjs/native/plugins/updates/src/index.d.ts +8 -0
- package/types/vendor/@akanjs/native/plugins/window/src/desktop.d.ts +4 -1
- package/types/vendor/@akanjs/native/plugins/window/src/index.d.ts +49 -0
- package/types/vendor/use-agentic/types.d.ts +2 -0
- package/types/webkit/CsrStack.d.ts +1 -1
- package/types/webkit/McpAppAgent.d.ts +14 -0
- package/types/webkit/McpAppApprovals.d.ts +8 -0
- package/types/webkit/McpAppBridge.d.ts +101 -0
- package/types/webkit/McpAppView.d.ts +21 -0
- package/types/webkit/index.d.ts +1 -0
- package/types/webkit/useLocation.d.ts +1 -1
- package/types/webkit/useVisualViewportFrame.d.ts +13 -0
- package/ui/Agent/Chat.tsx +29 -10
- package/ui/Agent/Composer.tsx +2 -1
- package/ui/Agent/EmbedApproval.tsx +17 -0
- package/ui/Agent/RichInput.tsx +1 -1
- package/ui/Agent/index.ts +2 -0
- package/ui/Agent/useKeyboardFrame.ts +10 -0
- package/ui/Agent/useScreenActions.ts +45 -0
- package/ui/BottomSheet.tsx +82 -53
- package/ui/Constant/Doc.tsx +113 -60
- package/ui/Constant/index.ts +2 -2
- package/ui/Constant/schemaDoc.ts +86 -26
- package/ui/Dialog/LegacyModal.tsx +53 -52
- package/ui/Dialog/Modal.tsx +93 -49
- package/ui/Dialog/modalSurface.ts +61 -0
- package/ui/DragAction.tsx +1 -1
- package/ui/DraggableList.tsx +1 -1
- package/ui/Dropdown.tsx +4 -6
- package/ui/KeyboardAvoiding.tsx +1 -1
- package/ui/Layout/BottomInset.tsx +20 -14
- package/ui/Layout/BottomTab.tsx +42 -16
- package/ui/Layout/FrameSlotFallback.tsx +59 -0
- package/ui/Layout/Navbar.tsx +37 -11
- package/ui/Layout/Sider.tsx +1 -1
- package/ui/Layout/TopInset.tsx +15 -14
- package/ui/Link/Action.tsx +15 -5
- package/ui/Link/Anchor.tsx +25 -13
- package/ui/Link/activePath.ts +25 -0
- package/ui/Link/index.tsx +1 -1
- package/ui/Load/Pagination.tsx +1 -1
- package/ui/Load/Units.tsx +1 -1
- package/ui/Loading/ProgressBar.tsx +1 -1
- package/ui/Modal.tsx +9 -0
- package/ui/Model/EditModal.tsx +16 -1
- package/ui/Model/New.tsx +25 -2
- package/ui/Model/NewWrapper_Client.tsx +2 -0
- package/ui/Model/ViewEditModal.tsx +9 -0
- package/ui/Model/ViewModal.tsx +16 -1
- package/ui/Model/newFormOrigin.ts +45 -0
- package/ui/Popconfirm.tsx +5 -7
- package/ui/Portal.tsx +4 -7
- package/ui/Reference/origin.ts +31 -0
- package/ui/ScreenNavigator.tsx +1 -1
- package/ui/Select.tsx +2 -7
- package/ui/Signal/Arg.tsx +2 -2
- package/ui/Signal/Doc.tsx +125 -63
- package/ui/Signal/Endpoint.tsx +11 -10
- package/ui/Signal/Listener.tsx +7 -10
- package/ui/Signal/Object.tsx +7 -14
- package/ui/Signal/Response.tsx +24 -24
- package/ui/Signal/RestApi.tsx +63 -52
- package/ui/Signal/WebSocket.tsx +65 -72
- package/ui/Signal/endpointEntries.ts +25 -0
- package/ui/Signal/index.ts +23 -6
- package/ui/Signal/signalText.ts +59 -0
- package/ui/System/CSR.tsx +34 -8
- package/ui/System/Client.tsx +36 -5
- package/ui/System/Common.tsx +76 -32
- package/ui/System/SSR.tsx +73 -31
- package/ui/Toast.tsx +1 -1
- package/ui/Tooltip.tsx +84 -30
- package/ui/index.ts +11 -1
- package/ui/overlayLayer.ts +25 -2
- package/ui/overlayPosition.ts +90 -0
- package/ui/recipe/inputRecipe.ts +8 -1
- package/ui/styles.css +152 -0
- package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeActivity.kt +22 -2
- package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeKernel.kt +37 -0
- package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeUpdateReports.kt +169 -0
- package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeUpdates.kt +110 -38
- package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeVectors.kt +20 -0
- package/vendor/@akanjs/native/native/desktop/Cargo.lock +16 -16
- package/vendor/@akanjs/native/native/desktop/src/external.rs +651 -0
- package/vendor/@akanjs/native/native/desktop/src/json.rs +23 -0
- package/vendor/@akanjs/native/native/desktop/src/lib.rs +228 -5
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeApp.swift +2 -1
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeKernel.swift +47 -1
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeUpdateReports.swift +145 -0
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeUpdates.swift +112 -43
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeVectors.swift +18 -0
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeViewController.swift +24 -2
- package/vendor/@akanjs/native/packages/cli/src/api.ts +22 -6
- package/vendor/@akanjs/native/packages/cli/src/commands/build.ts +24 -8
- package/vendor/@akanjs/native/packages/cli/src/commands/update.ts +11 -9
- package/vendor/@akanjs/native/packages/cli/src/config.ts +35 -7
- package/vendor/@akanjs/native/packages/cli/src/lib/compat.ts +2 -0
- package/vendor/@akanjs/native/packages/cli/src/lib/configkeys.ts +6 -4
- package/vendor/@akanjs/native/packages/cli/src/lib/desktop-server.ts +11 -1
- package/vendor/@akanjs/native/packages/cli/src/lib/distribution.ts +73 -0
- package/vendor/@akanjs/native/packages/cli/src/lib/project.ts +15 -3
- package/vendor/@akanjs/native/packages/cli/src/lib/publish.ts +70 -23
- package/vendor/@akanjs/native/packages/cli/src/lib/updates.ts +159 -23
- package/vendor/@akanjs/native/packages/cli/src/lib/vectors.ts +2 -0
- package/vendor/@akanjs/native/packages/cli/src/platforms/android.ts +9 -4
- package/vendor/@akanjs/native/packages/cli/src/platforms/desktop-update.ts +1 -1
- package/vendor/@akanjs/native/packages/cli/src/platforms/desktop.ts +4 -2
- package/vendor/@akanjs/native/packages/cli/src/platforms/ios.ts +9 -4
- package/vendor/@akanjs/native/packages/cli/src/platforms/windows-installer.ts +25 -11
- package/vendor/@akanjs/native/packages/cli/src/platforms/windows.ts +14 -3
- package/vendor/@akanjs/native/packages/core/src/kernel.ts +49 -0
- package/vendor/@akanjs/native/packages/core/vectors/updates.json +31 -0
- package/vendor/@akanjs/native/packages/desktop/src/main.ts +3 -0
- package/vendor/@akanjs/native/packages/desktop/src/plugin.ts +2 -0
- package/vendor/@akanjs/native/packages/desktop/src/server.ts +70 -6
- package/vendor/@akanjs/native/plugins/external-view/native-plugin.json +23 -0
- package/vendor/@akanjs/native/plugins/external-view/src/desktop.ts +321 -0
- package/vendor/@akanjs/native/plugins/external-view/src/index.ts +106 -0
- package/vendor/@akanjs/native/plugins/updates/android/UpdatesPlugin.kt +133 -41
- package/vendor/@akanjs/native/plugins/updates/ios/UpdatesPlugin.swift +101 -36
- package/vendor/@akanjs/native/plugins/updates/native-plugin.json +4 -2
- package/vendor/@akanjs/native/plugins/updates/src/desktop.ts +15 -1
- package/vendor/@akanjs/native/plugins/updates/src/index.ts +7 -1
- package/vendor/@akanjs/native/plugins/window/native-plugin.json +4 -0
- package/vendor/@akanjs/native/plugins/window/src/desktop.ts +68 -1
- package/vendor/@akanjs/native/plugins/window/src/index.ts +47 -0
- package/vendor/use-agentic/ToolRunner.ts +8 -1
- package/vendor/use-agentic/types.ts +2 -0
- package/webkit/CsrRouteTable.ts +1 -0
- package/webkit/CsrStack.ts +9 -2
- package/webkit/McpAppAgent.ts +138 -0
- package/webkit/McpAppApprovals.ts +56 -0
- package/webkit/McpAppBridge.ts +291 -0
- package/webkit/McpAppView.ts +258 -0
- package/webkit/bootCsr.tsx +54 -5
- package/webkit/index.ts +1 -0
- package/webkit/useCsrValues.ts +16 -10
- package/webkit/useFrameRuntime.ts +10 -1
- package/webkit/useLocation.ts +4 -3
- package/webkit/useVisualViewportFrame.tsx +52 -0
- package/service/predefinedAdaptor/sql/driver/libsql.ts +0 -45
- package/types/service/predefinedAdaptor/sql/driver/libsql.d.ts +0 -20
- package/types/ui/Agent/useKeyboardInset.d.ts +0 -1
- package/types/ui/ServerPortal.d.ts +0 -15
- package/ui/Agent/useKeyboardInset.ts +0 -19
- package/ui/ServerPortal.tsx +0 -49
|
@@ -6,12 +6,12 @@ import { NoDocumentError } from "akanjs/document";
|
|
|
6
6
|
import type { InjectRegistry, LiveRegistry } from "akanjs/service";
|
|
7
7
|
import type { Endpoint, EndpointCls } from "../../signal/endpoint";
|
|
8
8
|
import type { EndpointInfo } from "../../signal/endpointInfo";
|
|
9
|
-
import { Exception } from "../../signal/exception";
|
|
9
|
+
import { Exception, isExceptionLike } from "../../signal/exception";
|
|
10
10
|
import type { GuardCls } from "../../signal/guard";
|
|
11
11
|
import { McpDocument, type McpExposedEndpoint, type McpToolResult } from "../../signal/mcp";
|
|
12
12
|
import type { MiddlewareCls } from "../../signal/middleware";
|
|
13
13
|
import { SignalContext } from "../../signal/signalContext";
|
|
14
|
-
import { McpExecutionContext } from "./McpExecutionContext";
|
|
14
|
+
import { McpArgumentError, McpExecutionContext } from "./McpExecutionContext";
|
|
15
15
|
|
|
16
16
|
export class McpAuthRequiredError extends Error {}
|
|
17
17
|
|
|
@@ -38,25 +38,44 @@ export class McpDispatcher {
|
|
|
38
38
|
this.#props = props;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
async call(
|
|
41
|
+
async call(
|
|
42
|
+
exposed: McpExposedEndpoint,
|
|
43
|
+
args: Record<string, unknown>,
|
|
44
|
+
req: Request,
|
|
45
|
+
{ forView = false }: { forView?: boolean } = {},
|
|
46
|
+
): Promise<McpToolResult> {
|
|
42
47
|
const found = this.#index().get(exposed.key);
|
|
43
48
|
if (!found) return McpDispatcher.#failure(`Tool "${exposed.key}" is declared but not mounted on this server.`);
|
|
44
49
|
try {
|
|
45
|
-
const
|
|
50
|
+
const wire = await this.#exec(exposed.key, found, args, req);
|
|
51
|
+
const value = McpDispatcher.#readable(exposed, wire);
|
|
46
52
|
const structuredContent = McpDocument.structuredContent(exposed.endpoint, value);
|
|
47
53
|
return {
|
|
48
54
|
content: this.#content(structuredContent, value),
|
|
49
55
|
...(structuredContent === undefined ? {} : { structuredContent }),
|
|
56
|
+
...(forView ? { _meta: { "akanjs/value": wire ?? null } } : {}),
|
|
50
57
|
isError: false,
|
|
51
58
|
};
|
|
52
59
|
} catch (error) {
|
|
53
60
|
const status = McpDispatcher.#statusOf(error);
|
|
54
61
|
|
|
55
62
|
if ((status === 401 || status === 403) && !req.headers.get("authorization")) throw new McpAuthRequiredError();
|
|
56
|
-
|
|
63
|
+
const failure = McpDispatcher.#failure(this.#message(error, status, exposed.refName));
|
|
64
|
+
const restorable = forView ? McpDispatcher.#restorable(error, status) : null;
|
|
65
|
+
return restorable ? { ...failure, _meta: { "akanjs/error": restorable } } : failure;
|
|
57
66
|
}
|
|
58
67
|
}
|
|
59
68
|
|
|
69
|
+
static #restorable(error: unknown, status: number | undefined) {
|
|
70
|
+
if (!status || status >= 500 || status === 401 || status === 403 || error instanceof NoDocumentError) return null;
|
|
71
|
+
const data = (error as { data?: unknown }).data;
|
|
72
|
+
return {
|
|
73
|
+
error: error instanceof Error ? error.message : String(error),
|
|
74
|
+
statusCode: status,
|
|
75
|
+
...(data && typeof data === "object" ? { data } : {}),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
60
79
|
async filterForAccount<T extends { name: string }>(items: T[], req: Request): Promise<T[]> {
|
|
61
80
|
const index = this.#index();
|
|
62
81
|
|
|
@@ -115,6 +134,10 @@ export class McpDispatcher {
|
|
|
115
134
|
});
|
|
116
135
|
}
|
|
117
136
|
|
|
137
|
+
isEndpoint(name: string) {
|
|
138
|
+
return this.#index().has(name);
|
|
139
|
+
}
|
|
140
|
+
|
|
118
141
|
#index() {
|
|
119
142
|
if (this.#endpoints) return this.#endpoints;
|
|
120
143
|
const endpoints = new Map<string, { endpointInfo: EndpointInfo; endpoint: Endpoint }>();
|
|
@@ -146,8 +169,9 @@ export class McpDispatcher {
|
|
|
146
169
|
}
|
|
147
170
|
|
|
148
171
|
static #statusOf(error: unknown): number | undefined {
|
|
149
|
-
|
|
150
|
-
|
|
172
|
+
if (isExceptionLike(error) || error instanceof McpArgumentError || error instanceof NoDocumentError)
|
|
173
|
+
return error.statusCode;
|
|
174
|
+
return undefined;
|
|
151
175
|
}
|
|
152
176
|
|
|
153
177
|
#content(structuredContent: unknown, value: unknown): McpToolResult["content"] {
|
package/server/mcp/McpRouter.ts
CHANGED
|
@@ -7,7 +7,9 @@ import {
|
|
|
7
7
|
MCP_META_CLIENT_CAPABILITIES,
|
|
8
8
|
MCP_META_PROTOCOL_VERSION,
|
|
9
9
|
MCP_META_SERVER_INFO,
|
|
10
|
+
MCP_MODERN_VERSION,
|
|
10
11
|
MCP_SUPPORTED_VERSIONS,
|
|
12
|
+
MCP_UI_EXTENSION,
|
|
11
13
|
McpDocument,
|
|
12
14
|
type McpEra,
|
|
13
15
|
McpErrorCode,
|
|
@@ -15,14 +17,17 @@ import {
|
|
|
15
17
|
type McpJsonRpcRequest,
|
|
16
18
|
type McpOutputSchemaMode,
|
|
17
19
|
McpProgress,
|
|
20
|
+
type McpProtocolVersion,
|
|
18
21
|
type McpSignalCost,
|
|
19
22
|
type McpToolResult,
|
|
20
23
|
} from "../../signal/mcp";
|
|
24
|
+
import type { PromptMessage } from "../../signal/mcp/Msg";
|
|
21
25
|
import type { McpPrompt } from "../../signal/mcp/mcpProtocol";
|
|
22
|
-
import type { PagePromptEntry, PagePromptSource } from "../../signal/mcp/pagePrompt";
|
|
26
|
+
import type { PagePromptEntry, PagePromptRun, PagePromptSource } from "../../signal/mcp/pagePrompt";
|
|
23
27
|
import type { MiddlewareCls } from "../../signal/middleware";
|
|
24
28
|
import { FetchSerializer } from "../../signal/serializer";
|
|
25
29
|
import type { HttpRoutes } from "../types";
|
|
30
|
+
import { type McpAppShellSource, McpApps } from "./McpApps";
|
|
26
31
|
import { McpAuth, type McpAuthOption } from "./McpAuth";
|
|
27
32
|
import { McpAuthRequiredError, McpDispatcher } from "./McpDispatcher";
|
|
28
33
|
import { McpEventStream } from "./McpEventStream";
|
|
@@ -47,6 +52,9 @@ export interface McpRouterProps {
|
|
|
47
52
|
outputSchema?: McpOutputSchemaMode;
|
|
48
53
|
auth?: McpAuthOption;
|
|
49
54
|
rateLimit?: McpRateLimitOption | false;
|
|
55
|
+
apps?: boolean;
|
|
56
|
+
appShell?: McpAppShellSource;
|
|
57
|
+
modern?: boolean;
|
|
50
58
|
}
|
|
51
59
|
|
|
52
60
|
interface McpCall {
|
|
@@ -63,6 +71,8 @@ interface McpErrorOptions {
|
|
|
63
71
|
headers?: Record<string, string>;
|
|
64
72
|
}
|
|
65
73
|
|
|
74
|
+
type McpPageRun = { refusal: string } | { run: Extract<PagePromptRun, { ok: true }> | null; messages: PromptMessage[] };
|
|
75
|
+
|
|
66
76
|
interface McpCacheHint {
|
|
67
77
|
ttlMs: number;
|
|
68
78
|
cacheScope: "public" | "private";
|
|
@@ -85,7 +95,10 @@ export class McpRouter {
|
|
|
85
95
|
readonly #dispatcher: McpDispatcher;
|
|
86
96
|
readonly #auth: McpAuth;
|
|
87
97
|
readonly #limiter: McpRateLimiter | null;
|
|
98
|
+
readonly #apps: McpApps | null;
|
|
99
|
+
readonly #versions: readonly McpProtocolVersion[];
|
|
88
100
|
#document: McpDocument | null = null;
|
|
101
|
+
#pageListFailed = false;
|
|
89
102
|
|
|
90
103
|
constructor(props: McpRouterProps) {
|
|
91
104
|
this.#props = props;
|
|
@@ -94,6 +107,10 @@ export class McpRouter {
|
|
|
94
107
|
|
|
95
108
|
this.#dispatcher = new McpDispatcher({ ...props, legacyTextBlock, language: props.language ?? defaultLanguage });
|
|
96
109
|
this.#auth = new McpAuth({ ...props.auth, path: props.path ?? "/mcp" });
|
|
110
|
+
this.#apps = props.apps && props.pagePrompts ? new McpApps(props.appShell ?? null) : null;
|
|
111
|
+
|
|
112
|
+
const modern = props.modern ?? !this.#apps?.rendersUi;
|
|
113
|
+
this.#versions = modern ? MCP_SUPPORTED_VERSIONS : MCP_SUPPORTED_VERSIONS.filter((v) => v !== MCP_MODERN_VERSION);
|
|
97
114
|
this.#limiter =
|
|
98
115
|
props.rateLimit === false
|
|
99
116
|
? null
|
|
@@ -108,7 +125,7 @@ export class McpRouter {
|
|
|
108
125
|
[this.#props.path ?? "/mcp"]: {
|
|
109
126
|
POST: async (req: Request) => this.#cors(req, await this.#post(req)),
|
|
110
127
|
OPTIONS: (req: Request) => this.#preflight(req),
|
|
111
|
-
|
|
128
|
+
|
|
112
129
|
GET: (req: Request) => this.#cors(req, notAllowed()),
|
|
113
130
|
DELETE: (req: Request) => this.#cors(req, notAllowed()),
|
|
114
131
|
},
|
|
@@ -121,7 +138,8 @@ export class McpRouter {
|
|
|
121
138
|
const document = this.#getDocument();
|
|
122
139
|
const { tools, resourceTemplates, refusals, undescribed } = document;
|
|
123
140
|
const cost = document.listingCost;
|
|
124
|
-
const
|
|
141
|
+
const appTools = document.appTools.length ? ` appTools=${document.appTools.length}` : "";
|
|
142
|
+
const counts = `tools=${tools.length}${appTools} resourceTemplates=${resourceTemplates.length}`;
|
|
125
143
|
const readOnly = this.#props.readOnly ? " (read-only deployment)" : "";
|
|
126
144
|
McpRouter.logger.debug(`MCP catalogue: ${counts}${readOnly} · listing ${McpRouter.#kb(cost.bytes)}`);
|
|
127
145
|
if (cost.bySignal.length) McpRouter.logger.debug(`MCP catalogue cost: ${McpRouter.#costLine(cost.bySignal)}`);
|
|
@@ -142,6 +160,10 @@ export class McpRouter {
|
|
|
142
160
|
this.#reportPagePrompts();
|
|
143
161
|
this.#reportUnverifiable(tools.map((tool) => tool.name));
|
|
144
162
|
for (const { key, reason } of refusals) McpRouter.logger.verbose(`MCP did not expose "${key}": ${reason}`);
|
|
163
|
+
for (const { name } of document.appTools)
|
|
164
|
+
McpRouter.logger.verbose(
|
|
165
|
+
`MCP kept "${name}" for the views an MCP Apps host renders: it declares \`mcp: false\`, so no model is shown it.`,
|
|
166
|
+
);
|
|
145
167
|
for (const { key, reason } of undescribed)
|
|
146
168
|
McpRouter.logger.warn(`MCP exposed "${key}" with no description: ${reason}`);
|
|
147
169
|
} catch (error) {
|
|
@@ -185,6 +207,7 @@ export class McpRouter {
|
|
|
185
207
|
resolveDescription: (key) => lookup.text(key),
|
|
186
208
|
readOnly: this.#props.readOnly,
|
|
187
209
|
outputSchema: this.#props.outputSchema,
|
|
210
|
+
apps: !!this.#apps?.rendersUi,
|
|
188
211
|
});
|
|
189
212
|
return this.#document;
|
|
190
213
|
}
|
|
@@ -212,11 +235,21 @@ export class McpRouter {
|
|
|
212
235
|
|
|
213
236
|
const meta = McpRouter.#meta(params);
|
|
214
237
|
const era: McpEra = method !== "initialize" && meta && MCP_META_PROTOCOL_VERSION in meta ? "modern" : "legacy";
|
|
215
|
-
const
|
|
238
|
+
const discoverable = method !== "server/discover" || this.#versions.includes(MCP_MODERN_VERSION);
|
|
239
|
+
const rejection =
|
|
240
|
+
era === "modern" && discoverable ? this.#validateModern(req, method, params, meta ?? {}, id) : null;
|
|
216
241
|
if (rejection) return rejection;
|
|
217
242
|
|
|
243
|
+
const started = Date.now();
|
|
218
244
|
try {
|
|
219
|
-
|
|
245
|
+
const call = { id, method, params, era, req };
|
|
246
|
+
const res = await this.#dispatch(call);
|
|
247
|
+
const subject = typeof params.name === "string" ? params.name : typeof params.uri === "string" ? params.uri : "";
|
|
248
|
+
const client = `${era}${this.#apps ? (this.#uiCapable(call) ? ", ui" : ", text") : ""}`;
|
|
249
|
+
McpRouter.logger.verbose(
|
|
250
|
+
`MCP ${method}${subject ? ` ${subject}` : ""} (${client}) → ${res.status} (${Date.now() - started}ms)`,
|
|
251
|
+
);
|
|
252
|
+
return res;
|
|
220
253
|
} catch (error) {
|
|
221
254
|
if (error instanceof McpAuthRequiredError) return this.#auth.unauthorized(req);
|
|
222
255
|
McpRouter.logger.error(
|
|
@@ -231,40 +264,54 @@ export class McpRouter {
|
|
|
231
264
|
switch (call.method) {
|
|
232
265
|
case "initialize":
|
|
233
266
|
|
|
234
|
-
return this.#result(
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
267
|
+
return this.#result(
|
|
268
|
+
call,
|
|
269
|
+
{
|
|
270
|
+
protocolVersion: this.#negotiate(call.params.protocolVersion),
|
|
271
|
+
capabilities: this.#capabilities(document),
|
|
272
|
+
serverInfo: this.#serverInfo(),
|
|
273
|
+
...(this.#props.instructions ? { instructions: this.#props.instructions } : {}),
|
|
274
|
+
},
|
|
275
|
+
undefined,
|
|
276
|
+
this.#uiSession(call),
|
|
277
|
+
);
|
|
240
278
|
case "ping":
|
|
241
279
|
|
|
242
280
|
return this.#result(call, {});
|
|
243
281
|
case "server/discover":
|
|
282
|
+
|
|
283
|
+
if (!this.#versions.includes(MCP_MODERN_VERSION))
|
|
284
|
+
return McpRouter.#error(call.id, McpErrorCode.methodNotFound, this.#methodNotFound(call.method));
|
|
244
285
|
return this.#result(
|
|
245
286
|
call,
|
|
246
287
|
{
|
|
247
|
-
supportedVersions:
|
|
288
|
+
supportedVersions: this.#versions,
|
|
248
289
|
capabilities: this.#capabilities(document),
|
|
249
290
|
...(this.#props.instructions ? { instructions: this.#props.instructions } : {}),
|
|
250
291
|
},
|
|
251
292
|
discoverCache,
|
|
252
293
|
);
|
|
253
294
|
case "tools/list":
|
|
254
|
-
return await this.#list(call, "tools", document.tools);
|
|
295
|
+
return await this.#list(call, "tools", [...document.tools, ...(await this.#appTools(call, document))]);
|
|
255
296
|
case "resources/list":
|
|
256
|
-
return await this.#list(call, "resources", document.resources);
|
|
297
|
+
return await this.#list(call, "resources", [...document.resources, ...(await this.#appResources(call))]);
|
|
257
298
|
case "resources/templates/list":
|
|
258
299
|
return await this.#list(call, "resourceTemplates", document.resourceTemplates);
|
|
259
300
|
case "prompts/list":
|
|
260
|
-
return await this.#list(
|
|
261
|
-
call,
|
|
262
|
-
"prompts",
|
|
263
|
-
((await this.#props.pagePrompts?.list()) ?? []).map(McpRouter.#promptOf),
|
|
264
|
-
);
|
|
301
|
+
return await this.#list(call, "prompts", (await this.#pageEntries()).map(McpRouter.#promptOf));
|
|
265
302
|
case "tools/call": {
|
|
266
303
|
const slot = await this.#acquire(call);
|
|
267
|
-
|
|
304
|
+
if ("refused" in slot) return slot.refused;
|
|
305
|
+
let release = slot.release;
|
|
306
|
+
try {
|
|
307
|
+
const page = await this.#pageToolOf(call.params.name);
|
|
308
|
+
if (page) return await this.#pageToolCall(call, document, page);
|
|
309
|
+
|
|
310
|
+
release = () => {};
|
|
311
|
+
return await this.#toolsCall(call, document, slot.release);
|
|
312
|
+
} finally {
|
|
313
|
+
release();
|
|
314
|
+
}
|
|
268
315
|
}
|
|
269
316
|
case "prompts/get":
|
|
270
317
|
return await this.#limited(call, () => this.#promptsGet(call, document));
|
|
@@ -272,7 +319,7 @@ export class McpRouter {
|
|
|
272
319
|
return await this.#limited(call, () => this.#resourcesRead(call, document));
|
|
273
320
|
default:
|
|
274
321
|
|
|
275
|
-
return McpRouter.#error(call.id, McpErrorCode.methodNotFound,
|
|
322
|
+
return McpRouter.#error(call.id, McpErrorCode.methodNotFound, this.#methodNotFound(call.method), {
|
|
276
323
|
status: call.era === "modern" ? 404 : 200,
|
|
277
324
|
});
|
|
278
325
|
}
|
|
@@ -318,13 +365,16 @@ export class McpRouter {
|
|
|
318
365
|
try {
|
|
319
366
|
const name = call.params.name;
|
|
320
367
|
if (typeof name !== "string") return McpRouter.#error(call.id, McpErrorCode.invalidParams, "Missing tool name.");
|
|
321
|
-
|
|
368
|
+
|
|
369
|
+
const exposed = document.isAppOnly(name) && !this.#uiCapable(call) ? undefined : document.findTool(name);
|
|
322
370
|
|
|
323
371
|
if (!exposed) return McpRouter.#error(call.id, McpErrorCode.invalidParams, `Unknown tool: ${name}.`);
|
|
324
372
|
const args = McpRouter.#arguments(call.params);
|
|
325
373
|
if (!args) return McpRouter.#error(call.id, McpErrorCode.invalidParams, McpRouter.#badArguments);
|
|
326
374
|
const progressToken = McpRouter.#progressToken(call);
|
|
327
|
-
|
|
375
|
+
const option = { forView: this.#forView(call) };
|
|
376
|
+
if (progressToken === undefined)
|
|
377
|
+
return this.#result(call, await this.#dispatcher.call(exposed, args, call.req, option));
|
|
328
378
|
streaming = true;
|
|
329
379
|
return await this.#streamedToolCall(call, exposed, args, progressToken, release);
|
|
330
380
|
} finally {
|
|
@@ -340,7 +390,8 @@ export class McpRouter {
|
|
|
340
390
|
release: () => void,
|
|
341
391
|
) {
|
|
342
392
|
const channel = new McpProgress();
|
|
343
|
-
const
|
|
393
|
+
const option = { forView: this.#forView(call) };
|
|
394
|
+
const settled = McpProgress.run(channel, async () => await this.#dispatcher.call(exposed, args, call.req, option))
|
|
344
395
|
.then(
|
|
345
396
|
(result) => ({ result }),
|
|
346
397
|
(error: unknown) => ({ error }),
|
|
@@ -394,10 +445,13 @@ export class McpRouter {
|
|
|
394
445
|
}
|
|
395
446
|
|
|
396
447
|
#capabilities(document: McpDocument) {
|
|
448
|
+
const ui = !!this.#apps?.rendersUi;
|
|
397
449
|
return {
|
|
398
|
-
...(document.tools.length ? { tools: {} } : {}),
|
|
399
|
-
...(document.resources.length || document.resourceTemplates.length ? { resources: {} } : {}),
|
|
450
|
+
...(document.tools.length || this.#apps ? { tools: {} } : {}),
|
|
451
|
+
...(document.resources.length || document.resourceTemplates.length || ui ? { resources: {} } : {}),
|
|
400
452
|
...(this.#props.pagePrompts ? { prompts: {} } : {}),
|
|
453
|
+
|
|
454
|
+
...(ui ? { extensions: { [MCP_UI_EXTENSION]: {} } } : {}),
|
|
401
455
|
};
|
|
402
456
|
}
|
|
403
457
|
|
|
@@ -409,6 +463,13 @@ export class McpRouter {
|
|
|
409
463
|
.then((entries) => {
|
|
410
464
|
const names = entries.map((entry) => entry.name).join(", ");
|
|
411
465
|
McpRouter.logger.debug(`MCP page prompts: ${entries.length}${entries.length ? ` (${names})` : ""}`);
|
|
466
|
+
if (!this.#apps || !entries.length) return;
|
|
467
|
+
for (const { name } of entries.filter((entry) => this.#isEndpointName(entry.name)))
|
|
468
|
+
McpRouter.logger.warn(
|
|
469
|
+
`MCP did not publish page "${name}" as a tool: an endpoint already takes that name, so it stays a prompt only. Rename the prompt.`,
|
|
470
|
+
);
|
|
471
|
+
const ui = this.#apps.rendersUi ? "rendered from the CSR shell by an MCP Apps host" : "with no view to render";
|
|
472
|
+
McpRouter.logger.debug(`MCP page tools: ${ui}`);
|
|
412
473
|
})
|
|
413
474
|
.catch((error: unknown) => {
|
|
414
475
|
McpRouter.logger.warn(
|
|
@@ -435,12 +496,33 @@ export class McpRouter {
|
|
|
435
496
|
async #promptsGet(call: McpCall, document: McpDocument) {
|
|
436
497
|
const name = call.params.name;
|
|
437
498
|
if (typeof name !== "string") return McpRouter.#error(call.id, McpErrorCode.invalidParams, "Missing prompt name.");
|
|
499
|
+
const entry = await this.#pageEntry(name);
|
|
500
|
+
if (!entry) return McpRouter.#error(call.id, McpErrorCode.invalidParams, `Unknown prompt: ${name}.`);
|
|
501
|
+
const page = await this.#runPage(call, document, entry);
|
|
502
|
+
if (page instanceof Response) return page;
|
|
503
|
+
if ("refusal" in page) return McpRouter.#error(call.id, McpErrorCode.invalidParams, page.refusal);
|
|
504
|
+
return this.#result(call, { description: entry.description, messages: page.messages });
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
async #pageToolCall(call: McpCall, document: McpDocument, entry: PagePromptEntry) {
|
|
508
|
+
const apps = this.#apps;
|
|
509
|
+
const page = await this.#runPage(call, document, entry);
|
|
510
|
+
if (page instanceof Response) return page;
|
|
511
|
+
if ("refusal" in page)
|
|
512
|
+
return this.#result(call, { content: [{ type: "text", text: page.refusal }], isError: true });
|
|
513
|
+
if (!page.run || !apps) return this.#result(call, { content: page.messages.map((message) => message.content) });
|
|
514
|
+
const origin = this.#auth.publicOrigin(call.req);
|
|
515
|
+
const forView = this.#uiCapable(call) ? { account: McpApps.accountOf(call.req) } : null;
|
|
516
|
+
return this.#result(call, apps.resultOf(page.run, page.messages, origin, forView));
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
async #runPage(call: McpCall, document: McpDocument, entry: PagePromptEntry): Promise<Response | McpPageRun> {
|
|
438
520
|
const source = this.#props.pagePrompts;
|
|
439
|
-
const
|
|
440
|
-
if (!source
|
|
521
|
+
const { name } = entry;
|
|
522
|
+
if (!source) return { refusal: `Unknown prompt: ${name}.` };
|
|
441
523
|
const args = McpRouter.#arguments(call.params);
|
|
442
|
-
|
|
443
|
-
|
|
524
|
+
const strings = args ? McpRouter.#pageArguments(args) : null;
|
|
525
|
+
if (!strings) return McpRouter.#error(call.id, McpErrorCode.invalidParams, McpRouter.#badArguments);
|
|
444
526
|
const composer = new PagePromptComposer({
|
|
445
527
|
document,
|
|
446
528
|
budget: this.#props.promptBudget ?? PagePromptComposer.defaultBudget,
|
|
@@ -455,8 +537,7 @@ export class McpRouter {
|
|
|
455
537
|
const missing = entry.arguments
|
|
456
538
|
.filter((arg) => arg.required && strings[arg.name] === undefined)
|
|
457
539
|
.map((arg) => arg.name);
|
|
458
|
-
if (missing.length)
|
|
459
|
-
return this.#result(call, { description: entry.description, messages: composer.missing(entry, missing) });
|
|
540
|
+
if (missing.length) return { run: null, messages: composer.missing(entry, missing) };
|
|
460
541
|
const run = await source.run({
|
|
461
542
|
name,
|
|
462
543
|
arguments: strings,
|
|
@@ -476,14 +557,95 @@ export class McpRouter {
|
|
|
476
557
|
: run.reason === "not-found"
|
|
477
558
|
? "No screen exists for these arguments."
|
|
478
559
|
: "The page failed to load.";
|
|
479
|
-
return
|
|
560
|
+
return { refusal: message };
|
|
561
|
+
}
|
|
562
|
+
return { run, messages: await composer.compose(entry, run) };
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
static #pageArguments(args: Record<string, unknown>): Record<string, string> | null {
|
|
566
|
+
const strings: Record<string, string> = {};
|
|
567
|
+
const plain = (value: unknown) => ["string", "number", "boolean"].includes(typeof value);
|
|
568
|
+
for (const [key, value] of Object.entries(args)) {
|
|
569
|
+
if (value === null || value === undefined) continue;
|
|
570
|
+
if (plain(value)) strings[key] = String(value);
|
|
571
|
+
else if (Array.isArray(value) && value.every(plain)) strings[key] = value.join(",");
|
|
572
|
+
else return null;
|
|
573
|
+
}
|
|
574
|
+
return strings;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
async #pageEntries(): Promise<PagePromptEntry[]> {
|
|
578
|
+
const source = this.#props.pagePrompts;
|
|
579
|
+
if (!source) return [];
|
|
580
|
+
try {
|
|
581
|
+
const entries = await source.list();
|
|
582
|
+
this.#pageListFailed = false;
|
|
583
|
+
return entries;
|
|
584
|
+
} catch (error) {
|
|
585
|
+
if (!this.#pageListFailed)
|
|
586
|
+
McpRouter.logger.warn(
|
|
587
|
+
`MCP page prompts could not be listed, so they are left out: ${error instanceof Error ? error.message : error}`,
|
|
588
|
+
);
|
|
589
|
+
this.#pageListFailed = true;
|
|
590
|
+
return [];
|
|
480
591
|
}
|
|
481
|
-
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
async #pageEntry(name: unknown) {
|
|
595
|
+
if (typeof name !== "string") return undefined;
|
|
596
|
+
return (await this.#pageEntries()).find((candidate) => candidate.name === name);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
#isEndpointName(name: string) {
|
|
600
|
+
return !!this.#getDocument().findTool(name) || this.#dispatcher.isEndpoint(name);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
async #pageTools() {
|
|
604
|
+
if (!this.#apps) return [];
|
|
605
|
+
return (await this.#pageEntries()).filter((entry) => !this.#isEndpointName(entry.name));
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
async #pageToolOf(name: unknown) {
|
|
609
|
+
if (!this.#apps || typeof name !== "string" || this.#isEndpointName(name)) return undefined;
|
|
610
|
+
return await this.#pageEntry(name);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
async #appTools(call: McpCall, document: McpDocument) {
|
|
614
|
+
const apps = this.#apps;
|
|
615
|
+
if (!apps) return [];
|
|
616
|
+
const pages = (await this.#pageTools()).map((entry) => apps.toolOf(entry));
|
|
617
|
+
return this.#uiCapable(call) ? [...document.appTools, ...pages] : pages;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
async #appResources(call: McpCall) {
|
|
621
|
+
const apps = this.#apps;
|
|
622
|
+
if (!apps?.rendersUi || !this.#uiCapable(call)) return [];
|
|
623
|
+
return apps.resourcesOf(await this.#pageEntries());
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
#uiCapable(call: McpCall) {
|
|
627
|
+
if (!this.#apps?.rendersUi) return false;
|
|
628
|
+
if (call.era === "modern") return McpApps.declaresUi(McpRouter.#meta(call.params)?.[MCP_META_CLIENT_CAPABILITIES]);
|
|
629
|
+
return call.req.headers.get("mcp-session-id")?.startsWith(McpApps.sessionPrefix) ?? false;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
#forView(call: McpCall) {
|
|
633
|
+
return this.#uiCapable(call) && McpRouter.#meta(call.params)?.["akanjs/view"] === true;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
#uiSession(call: McpCall): Record<string, string> | undefined {
|
|
637
|
+
if (!this.#apps?.rendersUi || !McpApps.declaresUi(call.params.capabilities)) return undefined;
|
|
638
|
+
return { "mcp-session-id": `${McpApps.sessionPrefix}${crypto.randomUUID()}` };
|
|
482
639
|
}
|
|
483
640
|
|
|
484
641
|
async #resourcesRead(call: McpCall, document: McpDocument) {
|
|
485
642
|
const uri = call.params.uri;
|
|
486
643
|
if (typeof uri !== "string") return McpRouter.#error(call.id, McpErrorCode.invalidParams, "Missing resource uri.");
|
|
644
|
+
if (this.#apps && uri.startsWith(McpApps.scheme)) {
|
|
645
|
+
const contents = await this.#apps.read(uri, this.#auth.publicOrigin(call.req));
|
|
646
|
+
if (!contents) return McpRouter.#error(call.id, McpErrorCode.invalidParams, `Unknown resource: ${uri}.`);
|
|
647
|
+
return this.#result(call, { contents: [contents] });
|
|
648
|
+
}
|
|
487
649
|
const resolved = document.resolveResource(uri);
|
|
488
650
|
if (!resolved) return McpRouter.#error(call.id, McpErrorCode.invalidParams, `Unknown resource: ${uri}.`);
|
|
489
651
|
const result = await this.#dispatcher.call(resolved.exposed, resolved.args, call.req);
|
|
@@ -517,7 +679,7 @@ export class McpRouter {
|
|
|
517
679
|
new Response(null, {
|
|
518
680
|
status: 204,
|
|
519
681
|
headers: {
|
|
520
|
-
"access-control-allow-methods": "POST, OPTIONS",
|
|
682
|
+
"access-control-allow-methods": "POST, GET, DELETE, OPTIONS",
|
|
521
683
|
"access-control-allow-headers":
|
|
522
684
|
req.headers.get("access-control-request-headers") ?? "authorization, content-type",
|
|
523
685
|
"access-control-max-age": "600",
|
|
@@ -531,6 +693,7 @@ export class McpRouter {
|
|
|
531
693
|
if (!origin || !this.#originAllowed(req)) return res;
|
|
532
694
|
res.headers.set("access-control-allow-origin", origin);
|
|
533
695
|
res.headers.set("vary", "origin");
|
|
696
|
+
if (res.headers.has("mcp-session-id")) res.headers.set("access-control-expose-headers", "mcp-session-id");
|
|
534
697
|
return res;
|
|
535
698
|
}
|
|
536
699
|
|
|
@@ -539,8 +702,8 @@ export class McpRouter {
|
|
|
539
702
|
return { name: `${env.appName}-mcp`, version: this.#props.version ?? "0.0.0" };
|
|
540
703
|
}
|
|
541
704
|
|
|
542
|
-
#result(call: McpCall, result: object, cache?: McpCacheHint) {
|
|
543
|
-
return Response.json(this.#envelope(call, result, cache));
|
|
705
|
+
#result(call: McpCall, result: object, cache?: McpCacheHint, headers?: Record<string, string>) {
|
|
706
|
+
return Response.json(this.#envelope(call, result, cache), { headers });
|
|
544
707
|
}
|
|
545
708
|
|
|
546
709
|
#envelope(call: McpCall, result: object, cache?: McpCacheHint) {
|
|
@@ -577,18 +740,18 @@ export class McpRouter {
|
|
|
577
740
|
return meta && typeof meta === "object" ? (meta as Record<string, unknown>) : null;
|
|
578
741
|
}
|
|
579
742
|
|
|
580
|
-
|
|
743
|
+
#negotiate(requested: unknown) {
|
|
581
744
|
if (typeof requested !== "string") return MCP_LEGACY_VERSION;
|
|
582
|
-
if (
|
|
583
|
-
const [newest] =
|
|
584
|
-
return requested > newest ? newest :
|
|
745
|
+
if (this.#versions.includes(requested as McpProtocolVersion)) return requested;
|
|
746
|
+
const [newest] = this.#versions;
|
|
747
|
+
return requested > newest ? newest : this.#versions[this.#versions.length - 1];
|
|
585
748
|
}
|
|
586
749
|
|
|
587
|
-
|
|
588
|
-
return `Method not found: ${method}. This server speaks MCP ${
|
|
750
|
+
#methodNotFound(method: string) {
|
|
751
|
+
return `Method not found: ${method}. This server speaks MCP ${this.#versions.join(", ")}.`;
|
|
589
752
|
}
|
|
590
753
|
|
|
591
|
-
|
|
754
|
+
#validateModern(
|
|
592
755
|
req: Request,
|
|
593
756
|
method: string,
|
|
594
757
|
params: Record<string, unknown>,
|
|
@@ -598,10 +761,10 @@ export class McpRouter {
|
|
|
598
761
|
const version = meta[MCP_META_PROTOCOL_VERSION];
|
|
599
762
|
if (typeof version !== "string" || !(MCP_META_CLIENT_CAPABILITIES in meta))
|
|
600
763
|
return McpRouter.#error(id, McpErrorCode.invalidParams, "Missing required `_meta` fields.", { status: 400 });
|
|
601
|
-
if (!
|
|
764
|
+
if (!this.#versions.includes(version as McpProtocolVersion))
|
|
602
765
|
return McpRouter.#error(id, McpErrorCode.unsupportedProtocolVersion, "Unsupported protocol version.", {
|
|
603
766
|
status: 400,
|
|
604
|
-
data: { requested: version, supported:
|
|
767
|
+
data: { requested: version, supported: this.#versions },
|
|
605
768
|
});
|
|
606
769
|
const named = params.name ?? params.uri;
|
|
607
770
|
const mismatch =
|
package/server/ops/opsToken.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type KeyObject, verify } from "node:crypto";
|
|
2
|
+
import { Ed25519Key } from "../ed25519Key";
|
|
2
3
|
|
|
3
4
|
export interface OpsTokenClaims {
|
|
4
5
|
aud: string;
|
|
@@ -29,15 +30,7 @@ export class OpsTokenVerifier {
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
static parsePublicKey(value: string): KeyObject {
|
|
32
|
-
|
|
33
|
-
if (trimmed.startsWith("{")) return createPublicKey({ key: JSON.parse(trimmed) as JsonWebKey, format: "jwk" });
|
|
34
|
-
const pem = trimmed.includes("-----BEGIN") ? trimmed.replace(/\\n/g, "\n") : null;
|
|
35
|
-
if (pem) return createPublicKey(pem);
|
|
36
|
-
const raw = Buffer.from(trimmed, "base64url");
|
|
37
|
-
if (raw.length === 32)
|
|
38
|
-
return createPublicKey({ key: { kty: "OKP", crv: "Ed25519", x: raw.toString("base64url") }, format: "jwk" });
|
|
39
|
-
if (raw.length === 44) return createPublicKey({ key: raw, format: "der", type: "spki" });
|
|
40
|
-
throw new Error("AKAN_OPS_PUBLIC_KEY must be a PEM, a JWK, base64 SPKI DER, or a raw 32-byte base64url key");
|
|
33
|
+
return Ed25519Key.parsePublic(value, "AKAN_OPS_PUBLIC_KEY");
|
|
41
34
|
}
|
|
42
35
|
|
|
43
36
|
verifyRequest(req: Request, nowSec = Math.floor(Date.now() / 1000)): OpsTokenVerdict {
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
DatabaseAdaptorRole,
|
|
32
32
|
type DocumentStore,
|
|
33
33
|
ServiceModel,
|
|
34
|
+
type UpsertByIdOptions,
|
|
34
35
|
} from "akanjs/service";
|
|
35
36
|
import { Exception, getCurrentTrace, traceDataLoaderBatch } from "akanjs/signal";
|
|
36
37
|
|
|
@@ -293,6 +294,8 @@ export class DatabaseResolver {
|
|
|
293
294
|
updateById: (id: string, update: DocumentUpdateInput, options?: DocumentUpdateOptions) =>
|
|
294
295
|
timedQuery(() => store.updateOneByQuery({ id }, update, options)),
|
|
295
296
|
removeById: (id: string) => timedQuery(() => store.removeOneByQuery({ id })),
|
|
297
|
+
upsertById: (data: Record<string, unknown> & { id: string }, options?: UpsertByIdOptions) =>
|
|
298
|
+
timedQuery(() => store.upsertById(data, options)),
|
|
296
299
|
|
|
297
300
|
countDocuments: (query: QueryOf<any>) => timedQuery(() => store.count(query)),
|
|
298
301
|
bulkWrite: (
|