@belte/belte 0.19.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 +313 -0
- package/LICENSE +21 -0
- package/README.md +559 -0
- package/bin/belte.ts +183 -0
- package/package.json +110 -0
- package/src/App.svelte +31 -0
- package/src/appEntry.ts +151 -0
- package/src/assets/app.html +14 -0
- package/src/belteResolverPlugin.ts +858 -0
- package/src/build.ts +147 -0
- package/src/buildCli.ts +129 -0
- package/src/buildDisconnected.ts +122 -0
- package/src/bundleApp.ts +149 -0
- package/src/bundleDisconnectedEntry.ts +17 -0
- package/src/cliEntry.ts +25 -0
- package/src/clientBuildPlugins.ts +41 -0
- package/src/clientEntry.ts +7 -0
- package/src/compile.ts +64 -0
- package/src/controlServerWorker.ts +422 -0
- package/src/dedupeSveltePlugin.ts +66 -0
- package/src/devEntry.ts +169 -0
- package/src/discoveryEntry.ts +81 -0
- package/src/lib/browser/applyStreamedResolution.ts +33 -0
- package/src/lib/browser/cacheEntryFromSnapshot.ts +48 -0
- package/src/lib/browser/flushUnresolvedPlaceholders.ts +16 -0
- package/src/lib/browser/installStreamingPlaceholders.ts +32 -0
- package/src/lib/browser/openResolveStream.ts +42 -0
- package/src/lib/browser/page.svelte.ts +258 -0
- package/src/lib/browser/pageStreamController.ts +17 -0
- package/src/lib/browser/refetchPlaceholder.ts +12 -0
- package/src/lib/browser/remoteProxy.ts +37 -0
- package/src/lib/browser/socketChannel.ts +192 -0
- package/src/lib/browser/socketProxy.ts +57 -0
- package/src/lib/browser/startClient.ts +153 -0
- package/src/lib/browser/subscribe.ts +131 -0
- package/src/lib/browser/types/Errors.ts +9 -0
- package/src/lib/browser/types/Layouts.ts +7 -0
- package/src/lib/browser/types/Pages.ts +7 -0
- package/src/lib/browser/types/StreamingDeferred.ts +9 -0
- package/src/lib/bundle/BundleMenu.ts +11 -0
- package/src/lib/bundle/BundleMenuItem.ts +24 -0
- package/src/lib/bundle/BundleWindow.ts +36 -0
- package/src/lib/bundle/WEBVIEW_BUILD_REVISION.ts +9 -0
- package/src/lib/bundle/WEBVIEW_VERSION.ts +7 -0
- package/src/lib/bundle/bindConnectedFlag.ts +29 -0
- package/src/lib/bundle/bindRequestNavigate.ts +31 -0
- package/src/lib/bundle/buildWebviewLib.ts +111 -0
- package/src/lib/bundle/disconnected.css +9 -0
- package/src/lib/bundle/disconnected.svelte +386 -0
- package/src/lib/bundle/ensureWebviewLib.ts +20 -0
- package/src/lib/bundle/exitWithParent.ts +28 -0
- package/src/lib/bundle/infoPlist.ts +46 -0
- package/src/lib/bundle/installDownloads.ts +24 -0
- package/src/lib/bundle/installMacMenu.ts +39 -0
- package/src/lib/bundle/listenLocalControlServer.ts +19 -0
- package/src/lib/bundle/native/belteMenu.mm +422 -0
- package/src/lib/bundle/native/webview.h +4557 -0
- package/src/lib/bundle/onMenu.ts +41 -0
- package/src/lib/bundle/openWebview.ts +104 -0
- package/src/lib/bundle/pngToIcns.ts +47 -0
- package/src/lib/bundle/probeBelteServer.ts +34 -0
- package/src/lib/bundle/resolveServerBinary.ts +12 -0
- package/src/lib/bundle/resolveWebviewLib.ts +53 -0
- package/src/lib/bundle/serverBinaryFilename.ts +8 -0
- package/src/lib/bundle/signMacApp.ts +35 -0
- package/src/lib/bundle/spawnEmbeddedServer.ts +65 -0
- package/src/lib/bundle/stableLocalPort.ts +19 -0
- package/src/lib/bundle/waitForServer.ts +23 -0
- package/src/lib/bundle/webviewCachePath.ts +23 -0
- package/src/lib/bundle/webviewLibName.ts +11 -0
- package/src/lib/cli/connectToServer.ts +23 -0
- package/src/lib/cli/createClient.ts +170 -0
- package/src/lib/cli/dispatchCommand.ts +71 -0
- package/src/lib/cli/loadEnvFromBinaryDir.ts +16 -0
- package/src/lib/cli/parseArgvForRpc.ts +97 -0
- package/src/lib/cli/printHelp.ts +119 -0
- package/src/lib/cli/printSessionHelp.ts +27 -0
- package/src/lib/cli/printSessionStatus.ts +21 -0
- package/src/lib/cli/printTrimmed.ts +8 -0
- package/src/lib/cli/printValue.ts +10 -0
- package/src/lib/cli/resolveCliTarget.ts +48 -0
- package/src/lib/cli/runCli.ts +139 -0
- package/src/lib/cli/runSession.ts +105 -0
- package/src/lib/cli/startLocalInstance.ts +14 -0
- package/src/lib/cli/tokenizeLine.ts +51 -0
- package/src/lib/cli/types/CliManifest.ts +9 -0
- package/src/lib/cli/types/CliManifestEntry.ts +17 -0
- package/src/lib/cli/types/CliTarget.ts +13 -0
- package/src/lib/mcp/annotationsForMethod.ts +29 -0
- package/src/lib/mcp/createMcpResourceServer.ts +101 -0
- package/src/lib/mcp/createMcpServer.ts +42 -0
- package/src/lib/mcp/dispatchMcpRequest.ts +146 -0
- package/src/lib/mcp/mcpResourceServerSlot.ts +18 -0
- package/src/lib/mcp/mcpSurface.ts +265 -0
- package/src/lib/mcp/toolResultFromResponse.ts +66 -0
- package/src/lib/mcp/types/JsonRpcRequest.ts +12 -0
- package/src/lib/mcp/types/JsonRpcResponse.ts +20 -0
- package/src/lib/mcp/types/McpResourceContents.ts +10 -0
- package/src/lib/mcp/types/McpResourceDescriptor.ts +6 -0
- package/src/lib/mcp/types/McpResourceServer.ts +12 -0
- package/src/lib/mcp/types/McpServer.ts +9 -0
- package/src/lib/mcp/types/McpServerOptions.ts +16 -0
- package/src/lib/server/AppModule.ts +33 -0
- package/src/lib/server/DELETE.ts +9 -0
- package/src/lib/server/GET.ts +9 -0
- package/src/lib/server/HEAD.ts +9 -0
- package/src/lib/server/PATCH.ts +9 -0
- package/src/lib/server/POST.ts +9 -0
- package/src/lib/server/PUT.ts +9 -0
- package/src/lib/server/agent.ts +76 -0
- package/src/lib/server/appDataDir.ts +15 -0
- package/src/lib/server/cli/buildEnvContent.ts +19 -0
- package/src/lib/server/cli/createTarGz.ts +76 -0
- package/src/lib/server/cli/handleCliDownload.ts +153 -0
- package/src/lib/server/cli/handleCliInstall.ts +37 -0
- package/src/lib/server/cli/installScript.ts +29 -0
- package/src/lib/server/cli/maxSourceMtime.ts +26 -0
- package/src/lib/server/cookies.ts +29 -0
- package/src/lib/server/env.ts +50 -0
- package/src/lib/server/error.ts +70 -0
- package/src/lib/server/json.ts +28 -0
- package/src/lib/server/jsonl.ts +46 -0
- package/src/lib/server/prompts/definePrompt.ts +20 -0
- package/src/lib/server/prompts/promptRegistry.ts +9 -0
- package/src/lib/server/prompts/registerPrompt.ts +6 -0
- package/src/lib/server/prompts/renderPromptTemplate.ts +16 -0
- package/src/lib/server/prompts/types/Prompt.ts +13 -0
- package/src/lib/server/prompts/types/PromptOptions.ts +12 -0
- package/src/lib/server/prompts/types/PromptRegistryEntry.ts +13 -0
- package/src/lib/server/prompts/types/PromptRoutes.ts +10 -0
- package/src/lib/server/redirect.ts +42 -0
- package/src/lib/server/request.ts +18 -0
- package/src/lib/server/rpc/defineVerb.ts +133 -0
- package/src/lib/server/rpc/dispatchVerbInProcess.ts +46 -0
- package/src/lib/server/rpc/findVerbByCommandName.ts +18 -0
- package/src/lib/server/rpc/parseArgs.ts +95 -0
- package/src/lib/server/rpc/registerVerb.ts +6 -0
- package/src/lib/server/rpc/types/RemoteHandler.ts +27 -0
- package/src/lib/server/rpc/types/RemoteRoutes.ts +13 -0
- package/src/lib/server/rpc/types/TypedResponse.ts +18 -0
- package/src/lib/server/rpc/types/VerbHelper.ts +68 -0
- package/src/lib/server/rpc/types/VerbRegistryEntry.ts +29 -0
- package/src/lib/server/rpc/unprocessed.ts +14 -0
- package/src/lib/server/rpc/verbRegistry.ts +11 -0
- package/src/lib/server/runtime/DEFAULT_PORT.ts +6 -0
- package/src/lib/server/runtime/DEV_REBUILD_MESSAGE.ts +4 -0
- package/src/lib/server/runtime/DEV_RELOAD_CLIENT_SCRIPT.ts +29 -0
- package/src/lib/server/runtime/acceptsZstd.ts +8 -0
- package/src/lib/server/runtime/buildOpenApiSpec.ts +106 -0
- package/src/lib/server/runtime/cacheControlForAsset.ts +22 -0
- package/src/lib/server/runtime/containsTraversal.ts +37 -0
- package/src/lib/server/runtime/createAssetHeaderCache.ts +35 -0
- package/src/lib/server/runtime/createPublicAssetServer.ts +63 -0
- package/src/lib/server/runtime/createRouteDispatcher.ts +100 -0
- package/src/lib/server/runtime/createServer.ts +692 -0
- package/src/lib/server/runtime/devReloadResponse.ts +35 -0
- package/src/lib/server/runtime/disableIdleTimeoutForStream.ts +27 -0
- package/src/lib/server/runtime/envSchemaStore.ts +15 -0
- package/src/lib/server/runtime/findOpenPort.ts +35 -0
- package/src/lib/server/runtime/getActiveServer.ts +6 -0
- package/src/lib/server/runtime/globToPathSet.ts +29 -0
- package/src/lib/server/runtime/inProcessServer.ts +20 -0
- package/src/lib/server/runtime/internalErrorResponse.ts +25 -0
- package/src/lib/server/runtime/isCrossOriginUpgrade.ts +19 -0
- package/src/lib/server/runtime/listenOnOpenPort.ts +36 -0
- package/src/lib/server/runtime/logExposedSurfaces.ts +162 -0
- package/src/lib/server/runtime/mimeForExtension.ts +20 -0
- package/src/lib/server/runtime/parseIdleTimeout.ts +10 -0
- package/src/lib/server/runtime/parsePort.ts +11 -0
- package/src/lib/server/runtime/registryManifests.ts +66 -0
- package/src/lib/server/runtime/requestContext.ts +5 -0
- package/src/lib/server/runtime/resolveStreamResponse.ts +29 -0
- package/src/lib/server/runtime/runWithRequestScope.ts +57 -0
- package/src/lib/server/runtime/safeJsonForScript.ts +17 -0
- package/src/lib/server/runtime/serializeCacheSnapshot.ts +45 -0
- package/src/lib/server/runtime/serverSlot.ts +13 -0
- package/src/lib/server/runtime/setActiveServer.ts +6 -0
- package/src/lib/server/runtime/snapshotEntryFromCache.ts +81 -0
- package/src/lib/server/runtime/streamCacheResolutions.ts +37 -0
- package/src/lib/server/runtime/streamFromIterator.ts +86 -0
- package/src/lib/server/runtime/streamStash.ts +64 -0
- package/src/lib/server/runtime/types/Assets.ts +1 -0
- package/src/lib/server/runtime/types/RequestStore.ts +27 -0
- package/src/lib/server/runtime/withResponseDefaults.ts +24 -0
- package/src/lib/server/server.ts +32 -0
- package/src/lib/server/socket.ts +31 -0
- package/src/lib/server/sockets/createSocketDispatcher.ts +311 -0
- package/src/lib/server/sockets/defineSocket.ts +167 -0
- package/src/lib/server/sockets/lookupSocket.ts +6 -0
- package/src/lib/server/sockets/recentHistory.ts +11 -0
- package/src/lib/server/sockets/registerSocket.ts +6 -0
- package/src/lib/server/sockets/socketOperations.ts +35 -0
- package/src/lib/server/sockets/socketRegistry.ts +9 -0
- package/src/lib/server/sockets/types/Socket.ts +21 -0
- package/src/lib/server/sockets/types/SocketClientFrame.ts +18 -0
- package/src/lib/server/sockets/types/SocketOperation.ts +22 -0
- package/src/lib/server/sockets/types/SocketOptions.ts +22 -0
- package/src/lib/server/sockets/types/SocketRegistryEntry.ts +17 -0
- package/src/lib/server/sockets/types/SocketRoutes.ts +10 -0
- package/src/lib/server/sockets/types/SocketServerFrame.ts +15 -0
- package/src/lib/server/sse.ts +53 -0
- package/src/lib/shared/BELTE_PACKAGE_NAME.ts +7 -0
- package/src/lib/shared/CACHE_CONTROL_VALUES.ts +16 -0
- package/src/lib/shared/HttpError.ts +19 -0
- package/src/lib/shared/RESOLVE_STREAM_PATH.ts +7 -0
- package/src/lib/shared/STREAMING_CONTENT_TYPES.ts +11 -0
- package/src/lib/shared/activeCacheStore.ts +20 -0
- package/src/lib/shared/appDataDir.ts +34 -0
- package/src/lib/shared/belteImportName.ts +44 -0
- package/src/lib/shared/browserClientFlags.ts +10 -0
- package/src/lib/shared/buildRpcRequest.ts +70 -0
- package/src/lib/shared/bundleLayout.ts +36 -0
- package/src/lib/shared/bundled.ts +34 -0
- package/src/lib/shared/cache.ts +559 -0
- package/src/lib/shared/cacheStoreSlot.ts +16 -0
- package/src/lib/shared/canonicalJson.ts +63 -0
- package/src/lib/shared/carriesBodyArgs.ts +13 -0
- package/src/lib/shared/clearLastConnection.ts +7 -0
- package/src/lib/shared/commandNameForUrl.ts +17 -0
- package/src/lib/shared/createCacheStore.ts +75 -0
- package/src/lib/shared/createPushIterator.ts +93 -0
- package/src/lib/shared/createRemoteFunction.ts +99 -0
- package/src/lib/shared/decodeResponse.ts +47 -0
- package/src/lib/shared/detectTarget.ts +27 -0
- package/src/lib/shared/exeSuffix.ts +9 -0
- package/src/lib/shared/exitOnBuildFailure.ts +17 -0
- package/src/lib/shared/extraForwardHeaders.ts +16 -0
- package/src/lib/shared/fileStem.ts +9 -0
- package/src/lib/shared/findExportCallSite.ts +479 -0
- package/src/lib/shared/forwardHeaders.ts +41 -0
- package/src/lib/shared/getRemoteMeta.ts +5 -0
- package/src/lib/shared/globalCacheStore.ts +15 -0
- package/src/lib/shared/globalCacheStoreSlot.ts +14 -0
- package/src/lib/shared/importNamesToStrip.ts +13 -0
- package/src/lib/shared/invalidateEvent.ts +11 -0
- package/src/lib/shared/isCompileTarget.ts +15 -0
- package/src/lib/shared/isDebugEnabled.ts +23 -0
- package/src/lib/shared/isModuleNotFound.ts +16 -0
- package/src/lib/shared/isReadOnlyMethod.ts +14 -0
- package/src/lib/shared/isStreamingResponse.ts +11 -0
- package/src/lib/shared/jsonSchemaForPromptArguments.ts +29 -0
- package/src/lib/shared/jsonSchemaForSchema.ts +32 -0
- package/src/lib/shared/jsonlErrorFrame.ts +24 -0
- package/src/lib/shared/keyForRemoteCall.ts +29 -0
- package/src/lib/shared/lastConnectionPath.ts +7 -0
- package/src/lib/shared/loadEnvFile.ts +17 -0
- package/src/lib/shared/loadEnvFromDataDir.ts +15 -0
- package/src/lib/shared/loadSvelteConfig.ts +18 -0
- package/src/lib/shared/log.ts +104 -0
- package/src/lib/shared/manifestModule.ts +39 -0
- package/src/lib/shared/memoizeByKey.ts +24 -0
- package/src/lib/shared/nearestLayoutPrefix.ts +36 -0
- package/src/lib/shared/normalizeTarget.ts +10 -0
- package/src/lib/shared/pageUrlForFile.ts +14 -0
- package/src/lib/shared/parseBoundedEnvInt.ts +20 -0
- package/src/lib/shared/parseEnv.ts +30 -0
- package/src/lib/shared/parsePromptMarkdown.ts +34 -0
- package/src/lib/shared/parseRouteSegments.ts +22 -0
- package/src/lib/shared/prepareRpcModule.ts +59 -0
- package/src/lib/shared/prepareSocketModule.ts +49 -0
- package/src/lib/shared/programNameForPackage.ts +14 -0
- package/src/lib/shared/promptNameForFile.ts +10 -0
- package/src/lib/shared/queryStringFromArgs.ts +27 -0
- package/src/lib/shared/readEnvFile.ts +15 -0
- package/src/lib/shared/readLastConnection.ts +18 -0
- package/src/lib/shared/readPackageJson.ts +9 -0
- package/src/lib/shared/recordRemoteMeta.ts +5 -0
- package/src/lib/shared/remoteMetaStore.ts +16 -0
- package/src/lib/shared/resolveClientFlags.ts +20 -0
- package/src/lib/shared/responseErrorText.ts +9 -0
- package/src/lib/shared/rpcUrlForFile.ts +19 -0
- package/src/lib/shared/runningAsStandaloneBinary.ts +13 -0
- package/src/lib/shared/serializeEnv.ts +18 -0
- package/src/lib/shared/setCacheStoreResolver.ts +6 -0
- package/src/lib/shared/setGlobalCacheStoreResolver.ts +6 -0
- package/src/lib/shared/socketNameForFile.ts +11 -0
- package/src/lib/shared/sseErrorFrame.ts +29 -0
- package/src/lib/shared/streamResponse.ts +169 -0
- package/src/lib/shared/stripImport.ts +27 -0
- package/src/lib/shared/subscribableFromResponse.ts +51 -0
- package/src/lib/shared/toBunRoutePattern.ts +28 -0
- package/src/lib/shared/types/CacheEntry.ts +63 -0
- package/src/lib/shared/types/CacheInvalidation.ts +9 -0
- package/src/lib/shared/types/CacheOptions.ts +33 -0
- package/src/lib/shared/types/CacheSnapshot.ts +16 -0
- package/src/lib/shared/types/CacheSnapshotEntry.ts +15 -0
- package/src/lib/shared/types/CacheStore.ts +32 -0
- package/src/lib/shared/types/ClientFlags.ts +11 -0
- package/src/lib/shared/types/CompileTarget.ts +6 -0
- package/src/lib/shared/types/HttpVerb.ts +1 -0
- package/src/lib/shared/types/LastConnection.ts +9 -0
- package/src/lib/shared/types/PromptArgument.ts +12 -0
- package/src/lib/shared/types/RawRemoteFunction.ts +13 -0
- package/src/lib/shared/types/RemoteFunction.ts +42 -0
- package/src/lib/shared/types/StandardSchemaV1.ts +57 -0
- package/src/lib/shared/types/StreamedResolution.ts +10 -0
- package/src/lib/shared/types/StreamingPlaceholder.ts +13 -0
- package/src/lib/shared/types/Subscribable.ts +15 -0
- package/src/lib/shared/types/SvelteConfig.ts +5 -0
- package/src/lib/shared/withJsonSchema.ts +20 -0
- package/src/lib/shared/writeLastConnection.ts +13 -0
- package/src/lib/shared/writeRoutesDts.ts +67 -0
- package/src/lib/test/clearVerbRegistry.ts +11 -0
- package/src/lib/test/createTestClient.ts +78 -0
- package/src/preload.ts +20 -0
- package/src/scaffold.ts +92 -0
- package/src/serverBuildPlugins.ts +25 -0
- package/src/serverEntry.ts +94 -0
- package/src/sveltePlugin.ts +58 -0
- package/src/tailwindStylePreprocessor.ts +62 -0
- package/template/bunfig.toml +4 -0
- package/template/package.json +19 -0
- package/template/src/app.ts +23 -0
- package/template/src/browser/app.css +21 -0
- package/template/src/browser/app.html +24 -0
- package/template/src/browser/pages/about/page.svelte +5 -0
- package/template/src/browser/pages/layout.svelte +26 -0
- package/template/src/browser/pages/page.svelte +20 -0
- package/template/src/bundle/icon.png +0 -0
- package/template/src/cli/banner.txt +3 -0
- package/template/src/cli/footer.txt +1 -0
- package/template/src/server/config.ts +17 -0
- package/template/src/server/rpc/getHello.ts +35 -0
- package/template/svelte.config.js +12 -0
- package/template/tsconfig.json +18 -0
- package/tsconfig.app.json +16 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { McpResourceServer } from './types/McpResourceServer.ts'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Process-wide slot for the MCP resource server. createServer assigns it at
|
|
5
|
+
boot; dispatchMcpRequest reads it on resources/list + resources/read. Mirrors
|
|
6
|
+
the registryManifests slot — the default MCP server is constructed in the
|
|
7
|
+
belte:mcp virtual with no args, so the resource server (which needs the
|
|
8
|
+
project's resourcesDir + embedded map) is injected out of band.
|
|
9
|
+
*/
|
|
10
|
+
let resourceServer: McpResourceServer | undefined
|
|
11
|
+
|
|
12
|
+
export function setMcpResourceServer(value: McpResourceServer): void {
|
|
13
|
+
resourceServer = value
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getMcpResourceServer(): McpResourceServer | undefined {
|
|
17
|
+
return resourceServer
|
|
18
|
+
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { promptRegistry } from '../server/prompts/promptRegistry.ts'
|
|
2
|
+
import { dispatchVerbInProcess } from '../server/rpc/dispatchVerbInProcess.ts'
|
|
3
|
+
import { findVerbByCommandName } from '../server/rpc/findVerbByCommandName.ts'
|
|
4
|
+
import { verbRegistry } from '../server/rpc/verbRegistry.ts'
|
|
5
|
+
import { recentHistory } from '../server/sockets/recentHistory.ts'
|
|
6
|
+
import { socketOperations } from '../server/sockets/socketOperations.ts'
|
|
7
|
+
import { socketRegistry } from '../server/sockets/socketRegistry.ts'
|
|
8
|
+
import { commandNameForUrl } from '../shared/commandNameForUrl.ts'
|
|
9
|
+
import { forwardHeaders } from '../shared/forwardHeaders.ts'
|
|
10
|
+
import { jsonSchemaForSchema } from '../shared/jsonSchemaForSchema.ts'
|
|
11
|
+
import { annotationsForMethod } from './annotationsForMethod.ts'
|
|
12
|
+
import { getMcpResourceServer } from './mcpResourceServerSlot.ts'
|
|
13
|
+
import { toolResultFromResponse } from './toolResultFromResponse.ts'
|
|
14
|
+
import type { McpResourceContents } from './types/McpResourceContents.ts'
|
|
15
|
+
import type { McpResourceDescriptor } from './types/McpResourceDescriptor.ts'
|
|
16
|
+
|
|
17
|
+
/*
|
|
18
|
+
The app's MCP surface, projected for in-process consumers. This is the single
|
|
19
|
+
source of truth dispatchMcpRequest (the JSON-RPC-over-HTTP transport) and the
|
|
20
|
+
in-app agent loop (belte/server/agent) both build on — the same tool/prompt/
|
|
21
|
+
resource derivation, so a model reaching the app over HTTP and a model driven
|
|
22
|
+
in-process can't drift on what's exposed.
|
|
23
|
+
|
|
24
|
+
Internal: there is no package export. The public entry is `agent()`, which
|
|
25
|
+
calls `mcpSurface(request)` to hand an engine the gated tool set.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
export type ToolDescriptor = {
|
|
29
|
+
name: string
|
|
30
|
+
description: string
|
|
31
|
+
inputSchema: Record<string, unknown>
|
|
32
|
+
outputSchema?: Record<string, unknown>
|
|
33
|
+
annotations?: Record<string, boolean>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type PromptDescriptor = {
|
|
37
|
+
name: string
|
|
38
|
+
description?: string
|
|
39
|
+
arguments: Array<{ name: string; description?: string; required: boolean }>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// The MCP tools/call result shape (a text content block plus optional structuredContent).
|
|
43
|
+
export type ToolResult = Record<string, unknown>
|
|
44
|
+
|
|
45
|
+
// A prompt rendered to plain messages — the user turn(s) that seed a conversation.
|
|
46
|
+
export type PromptMessage = { role: 'user'; text: string }
|
|
47
|
+
|
|
48
|
+
export type McpSurface = {
|
|
49
|
+
tools: ToolDescriptor[]
|
|
50
|
+
call(name: string, args: Record<string, unknown> | undefined): Promise<ToolResult>
|
|
51
|
+
prompts: PromptDescriptor[]
|
|
52
|
+
getPrompt(name: string, args?: Record<string, unknown>): PromptMessage[]
|
|
53
|
+
listResources(): Promise<McpResourceDescriptor[]>
|
|
54
|
+
readResource(uri: string): Promise<McpResourceContents | undefined>
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/*
|
|
58
|
+
Builds the array of MCP tool descriptors.
|
|
59
|
+
|
|
60
|
+
RPCs: every verb with clients.mcp=true becomes one tool named after the
|
|
61
|
+
export's URL (folder segments joined with `-`). The HTTP verb feeds the
|
|
62
|
+
tool's annotations (readOnlyHint / destructiveHint / idempotentHint) so
|
|
63
|
+
a model can tell a read from a write; reads auto-expose while mutating
|
|
64
|
+
verbs require an explicit clients.mcp (see resolveClientFlags). When the
|
|
65
|
+
verb declares an `outputSchema` it's advertised as the tool outputSchema.
|
|
66
|
+
|
|
67
|
+
Sockets: every socket with clients.mcp=true contributes a `<base>-tail`
|
|
68
|
+
read tool (recent buffered messages) and, when clientPublish is set, a
|
|
69
|
+
`<base>-publish` tool.
|
|
70
|
+
*/
|
|
71
|
+
export function buildTools(): ToolDescriptor[] {
|
|
72
|
+
const tools: ToolDescriptor[] = []
|
|
73
|
+
for (const entry of verbRegistry.values()) {
|
|
74
|
+
if (!entry.clients.mcp) {
|
|
75
|
+
continue
|
|
76
|
+
}
|
|
77
|
+
/*
|
|
78
|
+
Tool description favours the schema's top-level description (the
|
|
79
|
+
vendor's JSON Schema conversion carries `.describe(...)` through),
|
|
80
|
+
falling back to `method url` so the tool is still labelled when
|
|
81
|
+
the schema has none.
|
|
82
|
+
*/
|
|
83
|
+
const inputSchema = jsonSchemaForSchema(entry.inputSchema)
|
|
84
|
+
const tool: ToolDescriptor = {
|
|
85
|
+
name: commandNameForUrl(entry.remote.url),
|
|
86
|
+
description:
|
|
87
|
+
(inputSchema.description as string | undefined) ??
|
|
88
|
+
`${entry.remote.method} ${entry.remote.url}`,
|
|
89
|
+
inputSchema,
|
|
90
|
+
annotations: annotationsForMethod(entry.remote.method),
|
|
91
|
+
}
|
|
92
|
+
if (entry.outputSchema) {
|
|
93
|
+
tool.outputSchema = jsonSchemaForSchema(entry.outputSchema)
|
|
94
|
+
}
|
|
95
|
+
tools.push(tool)
|
|
96
|
+
}
|
|
97
|
+
for (const entry of socketRegistry.values()) {
|
|
98
|
+
if (!entry.clients.mcp) {
|
|
99
|
+
continue
|
|
100
|
+
}
|
|
101
|
+
const payloadSchema = jsonSchemaForSchema(entry.schema)
|
|
102
|
+
for (const operation of socketOperations(entry)) {
|
|
103
|
+
if (operation.kind === 'tail') {
|
|
104
|
+
tools.push({
|
|
105
|
+
name: operation.name,
|
|
106
|
+
description: `Read recent messages from the "${operation.socketName}" socket`,
|
|
107
|
+
inputSchema: {
|
|
108
|
+
type: 'object',
|
|
109
|
+
properties: {
|
|
110
|
+
count: { type: 'number', description: 'max recent messages to return' },
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
outputSchema: {
|
|
114
|
+
type: 'object',
|
|
115
|
+
properties: { frames: { type: 'array', items: payloadSchema } },
|
|
116
|
+
},
|
|
117
|
+
annotations: { readOnlyHint: true, destructiveHint: false },
|
|
118
|
+
})
|
|
119
|
+
continue
|
|
120
|
+
}
|
|
121
|
+
tools.push({
|
|
122
|
+
name: operation.name,
|
|
123
|
+
description: `Publish a message to the "${operation.socketName}" socket`,
|
|
124
|
+
inputSchema: payloadSchema,
|
|
125
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false },
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return tools
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/*
|
|
133
|
+
MCP prompts derived from src/mcp/prompts. Arguments come from the JSON
|
|
134
|
+
Schema the resolver built from each prompt's frontmatter `arguments` list
|
|
135
|
+
(top-level properties + required flags); the model fills them in and the
|
|
136
|
+
framework interpolates them into the body on getPrompt.
|
|
137
|
+
*/
|
|
138
|
+
export function buildPrompts(): PromptDescriptor[] {
|
|
139
|
+
return Array.from(promptRegistry.values()).map((entry) => {
|
|
140
|
+
const jsonSchema = entry.jsonSchema ?? {}
|
|
141
|
+
const properties = (jsonSchema.properties ?? {}) as Record<string, { description?: string }>
|
|
142
|
+
const required = new Set((jsonSchema.required as string[] | undefined) ?? [])
|
|
143
|
+
return {
|
|
144
|
+
name: entry.prompt.name,
|
|
145
|
+
...(entry.prompt.description ? { description: entry.prompt.description } : {}),
|
|
146
|
+
arguments: Object.entries(properties).map(([argName, prop]) => ({
|
|
147
|
+
name: argName,
|
|
148
|
+
...(prop?.description ? { description: prop.description } : {}),
|
|
149
|
+
required: required.has(argName),
|
|
150
|
+
})),
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function textResult(text: string, isError = false): ToolResult {
|
|
156
|
+
return { content: [{ type: 'text', text }], ...(isError ? { isError: true } : {}) }
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/*
|
|
160
|
+
Dispatches the socket tail / publish tools by matching the tool name
|
|
161
|
+
against each mcp-exposed socket's operations (socketOperations is the same
|
|
162
|
+
projection tools/list advertised, so the publish op only exists when the
|
|
163
|
+
socket allows it). tail returns the recent history buffer (request/response
|
|
164
|
+
can't hold a live subscription); publish validates against the socket
|
|
165
|
+
schema and fans out. Returns undefined when the name isn't a known socket
|
|
166
|
+
tool so callTool can fall through to "unknown tool".
|
|
167
|
+
*/
|
|
168
|
+
function callSocketTool(
|
|
169
|
+
toolName: string,
|
|
170
|
+
args: Record<string, unknown> | undefined,
|
|
171
|
+
): ToolResult | undefined {
|
|
172
|
+
for (const entry of socketRegistry.values()) {
|
|
173
|
+
if (!entry.clients.mcp) {
|
|
174
|
+
continue
|
|
175
|
+
}
|
|
176
|
+
const operation = socketOperations(entry).find((op) => op.name === toolName)
|
|
177
|
+
if (!operation) {
|
|
178
|
+
continue
|
|
179
|
+
}
|
|
180
|
+
if (operation.kind === 'tail') {
|
|
181
|
+
const count = typeof args?.count === 'number' ? args.count : undefined
|
|
182
|
+
const frames = recentHistory(entry, count)
|
|
183
|
+
return {
|
|
184
|
+
content: [{ type: 'text', text: frames.map((f) => JSON.stringify(f)).join('\n') }],
|
|
185
|
+
structuredContent: { frames },
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
// publish() validates the payload against the socket schema and throws on failure.
|
|
190
|
+
entry.socket.publish(args)
|
|
191
|
+
} catch (error) {
|
|
192
|
+
return textResult(error instanceof Error ? error.message : String(error), true)
|
|
193
|
+
}
|
|
194
|
+
return textResult('ok')
|
|
195
|
+
}
|
|
196
|
+
return undefined
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/*
|
|
200
|
+
Tool dispatch. RPC tools synthesize a Request (with forwarded auth
|
|
201
|
+
headers from `inbound`) and pipe it through verb.fetch inside the request
|
|
202
|
+
scope — the same seam the HTTP router crosses, so validation, the handler,
|
|
203
|
+
and the request-scoped helpers (per-call cache(), cookies(), request())
|
|
204
|
+
behave identically. A handler throw is caught by the scope and framed as
|
|
205
|
+
an isError tool result (via the 500 response) rather than escaping. The
|
|
206
|
+
response (buffered or streaming) is framed by toolResultFromResponse.
|
|
207
|
+
Socket tools (`<base>-tail` / `<base>-publish`) fall through to the socket
|
|
208
|
+
dispatcher.
|
|
209
|
+
*/
|
|
210
|
+
export async function callTool(
|
|
211
|
+
toolName: string,
|
|
212
|
+
args: Record<string, unknown> | undefined,
|
|
213
|
+
inbound: Request,
|
|
214
|
+
): Promise<ToolResult> {
|
|
215
|
+
const entry = findVerbByCommandName(toolName)
|
|
216
|
+
if (entry?.clients.mcp) {
|
|
217
|
+
const response = await dispatchVerbInProcess({
|
|
218
|
+
entry,
|
|
219
|
+
args,
|
|
220
|
+
baseUrl: `${new URL(inbound.url).origin}/`,
|
|
221
|
+
headers: forwardHeaders(inbound.headers),
|
|
222
|
+
})
|
|
223
|
+
return toolResultFromResponse(response)
|
|
224
|
+
}
|
|
225
|
+
const socketResult = callSocketTool(toolName, args)
|
|
226
|
+
if (socketResult) {
|
|
227
|
+
return socketResult
|
|
228
|
+
}
|
|
229
|
+
throw new Error(`unknown tool: ${toolName}`)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/*
|
|
233
|
+
Renders a prompt to the message(s) that seed a conversation. A markdown
|
|
234
|
+
prompt is a single user turn whose text is the interpolated template.
|
|
235
|
+
Throws on an unknown prompt name.
|
|
236
|
+
*/
|
|
237
|
+
export function getPromptMessages(name: string, args?: Record<string, unknown>): PromptMessage[] {
|
|
238
|
+
const entry = promptRegistry.get(name)
|
|
239
|
+
if (!entry) {
|
|
240
|
+
throw new Error(`unknown prompt: ${name}`)
|
|
241
|
+
}
|
|
242
|
+
return [{ role: 'user', text: entry.prompt.render((args ?? {}) as Record<string, string>) }]
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/*
|
|
246
|
+
Projects the app's MCP surface for an in-process consumer bound to `request`
|
|
247
|
+
— tool calls forward that request's auth headers into the verb handler, so
|
|
248
|
+
the model acts with the caller's identity. Used by `agent()`.
|
|
249
|
+
*/
|
|
250
|
+
export function mcpSurface(request: Request): McpSurface {
|
|
251
|
+
return {
|
|
252
|
+
tools: buildTools(),
|
|
253
|
+
call: (name, args) => callTool(name, args, request),
|
|
254
|
+
prompts: buildPrompts(),
|
|
255
|
+
getPrompt: getPromptMessages,
|
|
256
|
+
async listResources() {
|
|
257
|
+
const server = getMcpResourceServer()
|
|
258
|
+
return server ? server.list() : []
|
|
259
|
+
},
|
|
260
|
+
async readResource(uri) {
|
|
261
|
+
const server = getMcpResourceServer()
|
|
262
|
+
return server ? server.read(uri) : undefined
|
|
263
|
+
},
|
|
264
|
+
}
|
|
265
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { decodeResponse } from '../shared/decodeResponse.ts'
|
|
2
|
+
import { isStreamingResponse } from '../shared/isStreamingResponse.ts'
|
|
3
|
+
import { responseErrorText } from '../shared/responseErrorText.ts'
|
|
4
|
+
import { streamResponse } from '../shared/streamResponse.ts'
|
|
5
|
+
|
|
6
|
+
// Frames a value as MCP text content — strings verbatim, everything else as JSON.
|
|
7
|
+
function asText(value: unknown): string {
|
|
8
|
+
return typeof value === 'string' ? value : JSON.stringify(value)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/*
|
|
12
|
+
Turns an rpc/socket Response into an MCP `tools/call` result. Always
|
|
13
|
+
carries a `text` content block for backward compatibility; adds
|
|
14
|
+
`structuredContent` (an object, per the MCP spec) so models that
|
|
15
|
+
understand structured output get the typed value instead of a stringified
|
|
16
|
+
blob.
|
|
17
|
+
|
|
18
|
+
- non-2xx → { content:[text], isError:true }
|
|
19
|
+
- sse/jsonl body → drained frame-by-frame; structuredContent = { frames }.
|
|
20
|
+
A mid-stream error surfaces as isError with the
|
|
21
|
+
frames collected so far.
|
|
22
|
+
- object body → structuredContent = the object.
|
|
23
|
+
- array/primitive → text only (structuredContent must be an object).
|
|
24
|
+
*/
|
|
25
|
+
export async function toolResultFromResponse(response: Response): Promise<Record<string, unknown>> {
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
return {
|
|
28
|
+
content: [{ type: 'text', text: await responseErrorText(response) }],
|
|
29
|
+
isError: true,
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (isStreamingResponse(response)) {
|
|
34
|
+
const frames: unknown[] = []
|
|
35
|
+
try {
|
|
36
|
+
for await (const frame of streamResponse(response)) {
|
|
37
|
+
frames.push(frame)
|
|
38
|
+
}
|
|
39
|
+
} catch (error) {
|
|
40
|
+
return {
|
|
41
|
+
content: [
|
|
42
|
+
{ type: 'text', text: frames.map(asText).join('\n') },
|
|
43
|
+
{
|
|
44
|
+
type: 'text',
|
|
45
|
+
text: `stream error: ${error instanceof Error ? error.message : String(error)}`,
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
structuredContent: { frames },
|
|
49
|
+
isError: true,
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
content: [{ type: 'text', text: frames.map(asText).join('\n') }],
|
|
54
|
+
structuredContent: { frames },
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const body = await decodeResponse(response)
|
|
59
|
+
const result: Record<string, unknown> = {
|
|
60
|
+
content: [{ type: 'text', text: asText(body) }],
|
|
61
|
+
}
|
|
62
|
+
if (body !== null && typeof body === 'object' && !Array.isArray(body)) {
|
|
63
|
+
result.structuredContent = body
|
|
64
|
+
}
|
|
65
|
+
return result
|
|
66
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/*
|
|
2
|
+
JSON-RPC 2.0 request frame as MCP delivers it over Streamable HTTP. The
|
|
3
|
+
`id` is absent for notifications (which we don't currently receive from
|
|
4
|
+
clients but accept silently). `method` is a string like "tools/list" or
|
|
5
|
+
"resources/read".
|
|
6
|
+
*/
|
|
7
|
+
export type JsonRpcRequest = {
|
|
8
|
+
jsonrpc: '2.0'
|
|
9
|
+
id?: string | number
|
|
10
|
+
method: string
|
|
11
|
+
params?: Record<string, unknown>
|
|
12
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/*
|
|
2
|
+
JSON-RPC 2.0 response frame. Exactly one of `result` / `error` is set
|
|
3
|
+
per request. The `id` echoes the inbound request id (null when the
|
|
4
|
+
request id was malformed and the error is being returned).
|
|
5
|
+
*/
|
|
6
|
+
export type JsonRpcResponse =
|
|
7
|
+
| {
|
|
8
|
+
jsonrpc: '2.0'
|
|
9
|
+
id: string | number | null
|
|
10
|
+
result: unknown
|
|
11
|
+
}
|
|
12
|
+
| {
|
|
13
|
+
jsonrpc: '2.0'
|
|
14
|
+
id: string | number | null
|
|
15
|
+
error: {
|
|
16
|
+
code: number
|
|
17
|
+
message: string
|
|
18
|
+
data?: unknown
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/*
|
|
2
|
+
One entry in an MCP resources/read result. Text-typed resources carry `text`;
|
|
3
|
+
everything else carries base64 `blob` — exactly one is present.
|
|
4
|
+
*/
|
|
5
|
+
export type McpResourceContents = {
|
|
6
|
+
uri: string
|
|
7
|
+
mimeType: string
|
|
8
|
+
text?: string
|
|
9
|
+
blob?: string
|
|
10
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { McpResourceContents } from './McpResourceContents.ts'
|
|
2
|
+
import type { McpResourceDescriptor } from './McpResourceDescriptor.ts'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
Serves the project's src/mcp/resources files to the MCP dispatcher. `list`
|
|
6
|
+
backs resources/list; `read` backs resources/read and resolves to undefined
|
|
7
|
+
for an unknown uri.
|
|
8
|
+
*/
|
|
9
|
+
export type McpResourceServer = {
|
|
10
|
+
list(): Promise<McpResourceDescriptor[]>
|
|
11
|
+
read(uri: string): Promise<McpResourceContents | undefined>
|
|
12
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Public shape returned by createMcpServer. The bun route handler at
|
|
3
|
+
/__belte/mcp delegates inbound requests to `handle(request)`, which
|
|
4
|
+
parses the JSON-RPC envelope, dispatches to tools/resources, and returns
|
|
5
|
+
a Response carrying the JSON-RPC reply.
|
|
6
|
+
*/
|
|
7
|
+
export type McpServer = {
|
|
8
|
+
handle(request: Request): Promise<Response>
|
|
9
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/*
|
|
2
|
+
User-facing options for createMcpServer. All fields optional — the
|
|
3
|
+
zero-arg call works for any belte project (server info is derived from
|
|
4
|
+
package.json by the bundler when MCP is wired into createServer).
|
|
5
|
+
|
|
6
|
+
- `name` / `version`: identify the server in the MCP `initialize`
|
|
7
|
+
response. Defaults come from the project's package.json.
|
|
8
|
+
- `authorize`: optional boundary check. Runs once per MCP request before
|
|
9
|
+
any tool/resource dispatch. Throw HttpError (or any Error) to reject.
|
|
10
|
+
Per-tool authorization stays in the underlying verb handler.
|
|
11
|
+
*/
|
|
12
|
+
export type McpServerOptions = {
|
|
13
|
+
name?: string
|
|
14
|
+
version?: string
|
|
15
|
+
authorize?: (request: Request) => Promise<void> | void
|
|
16
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Server } from 'bun'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Optional hooks exported from src/app.ts. All hooks are optional; defaults
|
|
5
|
+
kick in when an export is missing. init returns an optional cleanup
|
|
6
|
+
function that runs on SIGINT/SIGTERM. handle is single-middleware with
|
|
7
|
+
next so user code can mutate the response or branch on the URL.
|
|
8
|
+
|
|
9
|
+
WebSockets are not exposed here — belte's only native WebSocket
|
|
10
|
+
surface is the sockets hub (see `belte/server/socket`), multiplexed onto a
|
|
11
|
+
single framework-owned connection per client at `/__belte/sockets`.
|
|
12
|
+
Inside request scopes, the live Bun.Server is reachable via the
|
|
13
|
+
exported `server()` function from `belte/server`; `init` receives it
|
|
14
|
+
explicitly because it runs outside a request.
|
|
15
|
+
*/
|
|
16
|
+
export type AppModule = {
|
|
17
|
+
/*
|
|
18
|
+
Extra inbound header names to forward onto in-process rpc Requests, on
|
|
19
|
+
top of belte's built-in auth/identity set (cookie, authorization, the
|
|
20
|
+
x-forwarded-* hints). Names a handler reads during SSR or an MCP call
|
|
21
|
+
that aren't in the default allowlist — e.g. 'accept-language',
|
|
22
|
+
'x-request-id', a custom 'x-tenant-id'. Case-insensitive.
|
|
23
|
+
*/
|
|
24
|
+
forwardHeaders?: string[]
|
|
25
|
+
init?: (ctx: {
|
|
26
|
+
server: Server<unknown>
|
|
27
|
+
}) => void | (() => void | Promise<void>) | Promise<void | (() => void | Promise<void>)>
|
|
28
|
+
handle?: (
|
|
29
|
+
request: Request,
|
|
30
|
+
next: (req: Request) => Promise<Response>,
|
|
31
|
+
) => Promise<Response> | Response
|
|
32
|
+
handleError?: (error: unknown, request: Request) => Promise<Response> | Response
|
|
33
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { VerbHelper } from './rpc/types/VerbHelper.ts'
|
|
2
|
+
import { unprocessed } from './rpc/unprocessed.ts'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
DELETE verb helper. The bundler rewrites every `export const x = DELETE(fn)` inside
|
|
6
|
+
`src/server/rpc/<file>.ts` into a defineVerb call (server target) or a
|
|
7
|
+
remoteProxy stub (client target). Calling this directly throws.
|
|
8
|
+
*/
|
|
9
|
+
export const DELETE: VerbHelper = (_fn: any, _opts?: any) => unprocessed('DELETE')
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { VerbHelper } from './rpc/types/VerbHelper.ts'
|
|
2
|
+
import { unprocessed } from './rpc/unprocessed.ts'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
GET verb helper. The bundler rewrites every `export const x = GET(fn)` inside
|
|
6
|
+
`src/server/rpc/<file>.ts` into a defineVerb call (server target) or a
|
|
7
|
+
remoteProxy stub (client target). Calling this directly throws.
|
|
8
|
+
*/
|
|
9
|
+
export const GET: VerbHelper = (_fn: any, _opts?: any) => unprocessed('GET')
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { VerbHelper } from './rpc/types/VerbHelper.ts'
|
|
2
|
+
import { unprocessed } from './rpc/unprocessed.ts'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
HEAD verb helper. The bundler rewrites every `export const x = HEAD(fn)` inside
|
|
6
|
+
`src/server/rpc/<file>.ts` into a defineVerb call (server target) or a
|
|
7
|
+
remoteProxy stub (client target). Calling this directly throws.
|
|
8
|
+
*/
|
|
9
|
+
export const HEAD: VerbHelper = (_fn: any, _opts?: any) => unprocessed('HEAD')
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { VerbHelper } from './rpc/types/VerbHelper.ts'
|
|
2
|
+
import { unprocessed } from './rpc/unprocessed.ts'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
PATCH verb helper. The bundler rewrites every `export const x = PATCH(fn)` inside
|
|
6
|
+
`src/server/rpc/<file>.ts` into a defineVerb call (server target) or a
|
|
7
|
+
remoteProxy stub (client target). Calling this directly throws.
|
|
8
|
+
*/
|
|
9
|
+
export const PATCH: VerbHelper = (_fn: any, _opts?: any) => unprocessed('PATCH')
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { VerbHelper } from './rpc/types/VerbHelper.ts'
|
|
2
|
+
import { unprocessed } from './rpc/unprocessed.ts'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
POST verb helper. The bundler rewrites every `export const x = POST(fn)` inside
|
|
6
|
+
`src/server/rpc/<file>.ts` into a defineVerb call (server target) or a
|
|
7
|
+
remoteProxy stub (client target). Calling this directly throws.
|
|
8
|
+
*/
|
|
9
|
+
export const POST: VerbHelper = (_fn: any, _opts?: any) => unprocessed('POST')
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { VerbHelper } from './rpc/types/VerbHelper.ts'
|
|
2
|
+
import { unprocessed } from './rpc/unprocessed.ts'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
PUT verb helper. The bundler rewrites every `export const x = PUT(fn)` inside
|
|
6
|
+
`src/server/rpc/<file>.ts` into a defineVerb call (server target) or a
|
|
7
|
+
remoteProxy stub (client target). Calling this directly throws.
|
|
8
|
+
*/
|
|
9
|
+
export const PUT: VerbHelper = (_fn: any, _opts?: any) => unprocessed('PUT')
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { McpSurface } from '../mcp/mcpSurface.ts'
|
|
2
|
+
import { mcpSurface } from '../mcp/mcpSurface.ts'
|
|
3
|
+
import { request } from './request.ts'
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
The in-app agent surface. `agent(engine, messages)` runs a model engine
|
|
7
|
+
against the app's own MCP surface and returns the engine's frame stream —
|
|
8
|
+
it does NOT pick a transport. The handler wraps it in `jsonl()` or `sse()`,
|
|
9
|
+
so consumption is the app's choice, same as any other streaming verb:
|
|
10
|
+
|
|
11
|
+
// src/server/rpc/chat.ts
|
|
12
|
+
import { agent } from '@belte/belte/server/agent'
|
|
13
|
+
import { jsonl } from '@belte/belte/server/jsonl'
|
|
14
|
+
import { engine } from '@belte/anthropic'
|
|
15
|
+
|
|
16
|
+
const chatEngine = engine({ model: 'claude-opus-4-8', apiKey: config.ANTHROPIC_API_KEY })
|
|
17
|
+
export const chat = POST(({ messages }) => jsonl(agent(chatEngine, messages)), { inputSchema })
|
|
18
|
+
|
|
19
|
+
The engine — provider-specific, lives in a `@belte/<provider>` package —
|
|
20
|
+
only sees the surface in and yields frames out, so swapping providers never
|
|
21
|
+
touches the verb or the UI.
|
|
22
|
+
|
|
23
|
+
Permission is decided server-side, not negotiated at runtime: the surface
|
|
24
|
+
is already gated by each verb's `clients.mcp` declaration plus its own
|
|
25
|
+
per-call handler auth, and any provider built-ins (e.g. Claude Code's bash
|
|
26
|
+
tool) are fenced by static rules in the engine's config.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
// A turn in the conversation, provider-neutral. The engine maps these to its provider's wire shape.
|
|
30
|
+
export type NeutralMessage =
|
|
31
|
+
| { role: 'user'; text: string }
|
|
32
|
+
| {
|
|
33
|
+
role: 'assistant'
|
|
34
|
+
text?: string
|
|
35
|
+
toolUses?: { id: string; name: string; input: unknown }[]
|
|
36
|
+
}
|
|
37
|
+
| { role: 'tool'; results: { id: string; content: string; isError?: boolean }[] }
|
|
38
|
+
|
|
39
|
+
// What the engine streams out; the handler frames it via jsonl()/sse() for the client.
|
|
40
|
+
export type AgentFrame =
|
|
41
|
+
| { type: 'text'; delta: string }
|
|
42
|
+
| { type: 'tool_use'; id: string; name: string; input: unknown }
|
|
43
|
+
| { type: 'tool_result'; id: string; name: string; ok: boolean }
|
|
44
|
+
| { type: 'done'; stop: 'end' | 'tool_use' | 'max_tokens' | 'refusal' }
|
|
45
|
+
|
|
46
|
+
// The app's tool/prompt/resource surface handed to an engine (already gated).
|
|
47
|
+
export type AgentSurface = McpSurface
|
|
48
|
+
|
|
49
|
+
/*
|
|
50
|
+
A model engine: surface + conversation in, frames out. It owns its own loop
|
|
51
|
+
(a raw-model tool loop, or driving a full agent harness) — core only sees
|
|
52
|
+
the frame stream. `origin` lets engines that reach the MCP endpoint over
|
|
53
|
+
HTTP address this server. Implementations live in `@belte/<provider>`
|
|
54
|
+
packages.
|
|
55
|
+
*/
|
|
56
|
+
export type AgentEngine = (input: {
|
|
57
|
+
surface: AgentSurface
|
|
58
|
+
messages: NeutralMessage[]
|
|
59
|
+
origin: string
|
|
60
|
+
}) => AsyncIterable<AgentFrame>
|
|
61
|
+
|
|
62
|
+
/*
|
|
63
|
+
Runs an engine against the current request's MCP surface and returns its
|
|
64
|
+
AgentFrame stream. Must be called inside a verb's request scope —
|
|
65
|
+
mcpSurface() forwards the caller's auth into every tool dispatch. The
|
|
66
|
+
handler chooses the transport: `jsonl(agent(engine, messages))` or
|
|
67
|
+
`sse(agent(engine, messages))`.
|
|
68
|
+
*/
|
|
69
|
+
export function agent(engine: AgentEngine, messages: NeutralMessage[]): AsyncIterable<AgentFrame> {
|
|
70
|
+
const inbound = request()
|
|
71
|
+
return engine({
|
|
72
|
+
surface: mcpSurface(inbound),
|
|
73
|
+
messages,
|
|
74
|
+
origin: new URL(inbound.url).origin,
|
|
75
|
+
})
|
|
76
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// @ts-expect-error virtual module resolved by belteResolverPlugin
|
|
2
|
+
import cliProgramName from '../../_virtual/cli-name.ts'
|
|
3
|
+
import { appDataDir as appDataDirForName } from '../shared/appDataDir.ts'
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
The running bundle's per-user data dir — keyed by the same program name belte
|
|
7
|
+
uses for the user's `.env` and `last-connection.json`, so an app's DB/cache lands
|
|
8
|
+
beside belte's own config instead of a drifted sibling directory. cwd-independent:
|
|
9
|
+
the path derives from the bundler-injected program name, not the process working
|
|
10
|
+
directory (which `open` sets to `/`). Pure: computes the path, never touches the
|
|
11
|
+
filesystem.
|
|
12
|
+
*/
|
|
13
|
+
export function appDataDir(): string {
|
|
14
|
+
return appDataDirForName(cliProgramName)
|
|
15
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { serializeEnv } from '../../shared/serializeEnv.ts'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Generates the `.env` file content shipped alongside the CLI binary in
|
|
5
|
+
the download tarball. BELTE_APP_URL is always present (derived from the
|
|
6
|
+
inbound request's origin); BELTE_APP_TOKEN is included only when the inbound
|
|
7
|
+
request carried an Authorization: Bearer header, so an authenticated
|
|
8
|
+
download bakes the caller's credential into the binary's env.
|
|
9
|
+
|
|
10
|
+
Tokens forward verbatim — the framework doesn't issue or refresh; the
|
|
11
|
+
user's auth code at the actual RPC endpoints validates whatever value
|
|
12
|
+
arrives back in subsequent calls.
|
|
13
|
+
*/
|
|
14
|
+
export function buildEnvContent(appUrl: string, bearerToken: string | undefined): string {
|
|
15
|
+
return serializeEnv({
|
|
16
|
+
BELTE_APP_URL: appUrl,
|
|
17
|
+
...(bearerToken ? { BELTE_APP_TOKEN: bearerToken } : {}),
|
|
18
|
+
})
|
|
19
|
+
}
|