@happyvertical/smrt-app-mcp 0.57.43 → 0.57.44
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/AGENTS.md +6 -0
- package/README.md +41 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +12 -4
- package/dist/auth.js.map +1 -1
- package/dist/chunks/errors-XG_zn1pP.js +73 -0
- package/dist/chunks/errors-XG_zn1pP.js.map +1 -0
- package/dist/chunks/identity-BNzyRsWf.js +14 -0
- package/dist/chunks/identity-BNzyRsWf.js.map +1 -0
- package/dist/chunks/{protocol-CoCuR_w7.js → protocol-BRQji5k_.js} +110 -48
- package/dist/chunks/protocol-BRQji5k_.js.map +1 -0
- package/dist/errors.d.ts +42 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/host-operations.d.ts +117 -0
- package/dist/host-operations.d.ts.map +1 -0
- package/dist/host-operations.js +160 -0
- package/dist/host-operations.js.map +1 -0
- package/dist/identity.d.ts +11 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/server.d.ts +46 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +6 -5
- package/dist/sveltekit.d.ts.map +1 -1
- package/dist/sveltekit.js +5 -3
- package/dist/sveltekit.js.map +1 -1
- package/dist/workflow-tools.d.ts +8 -0
- package/dist/workflow-tools.d.ts.map +1 -1
- package/package.json +7 -3
- package/dist/chunks/protocol-CoCuR_w7.js.map +0 -1
package/dist/smrt-knowledge.json
CHANGED
|
@@ -3,17 +3,18 @@
|
|
|
3
3
|
"sensitiveFieldsExcluded": true,
|
|
4
4
|
"generatedAt": "1970-01-01T00:00:00.000Z",
|
|
5
5
|
"packageName": "@happyvertical/smrt-app-mcp",
|
|
6
|
-
"packageVersion": "0.57.
|
|
6
|
+
"packageVersion": "0.57.44",
|
|
7
7
|
"sourceManifestPath": "dist/manifest.json",
|
|
8
8
|
"agentDocPath": "AGENTS.md",
|
|
9
9
|
"sourceHashes": {
|
|
10
|
-
"manifest": "
|
|
11
|
-
"packageJson": "
|
|
12
|
-
"agents": "
|
|
10
|
+
"manifest": "6cbd357af88975c26a8b54a4021125fbfae4b3582b5f946adeb32aa1ff84e8b1",
|
|
11
|
+
"packageJson": "14bc0430f90d4b57136986c51bdfd65314a4d775ed22b3713335e17ddda0ea75",
|
|
12
|
+
"agents": "ec2a9bcb2005a1906619a9cb2c6ada6cbba5b80842eb9caa49194e799b4659aa"
|
|
13
13
|
},
|
|
14
14
|
"exports": [
|
|
15
15
|
".",
|
|
16
16
|
"./auth",
|
|
17
|
+
"./host-operations",
|
|
17
18
|
"./sveltekit"
|
|
18
19
|
],
|
|
19
20
|
"dependencies": {
|
|
@@ -59,5 +60,5 @@
|
|
|
59
60
|
"polymorphicAssociations": 0,
|
|
60
61
|
"uuidColumns": 0
|
|
61
62
|
},
|
|
62
|
-
"agentDoc": "# @happyvertical/smrt-app-mcp\n\nApplication-scoped MCP server wrapper for SMRT apps. The core owns generated\ntool policy; `./sveltekit` owns the stateless Streamable HTTP transport.\n\n## Purpose\n\n- Wraps `@happyvertical/smrt-core` MCP generation with app-level tool allow-lists.\n- Filters unauthenticated tool visibility to public read-only tools.\n- Applies an optional principal-aware policy consistently to discovery and calls.\n- Provides workflow assertions so app servers can reject unsafe or incomplete tool calls before dispatch.\n- Exposes an opt-in durable Tasks extension through `@happyvertical/smrt-jobs`.\n\n## Patterns\n\n- Generate tools from core scoped to the allow-list (`MCPConfig.classNames`),\n then filter by allowed class names: a duplicate tool name among registered\n classes the app never publishes must not fail its catalog or calls (#3490).\n `mountMcpAppRoute`/`createDefaultMcpAppServer` resolve each model\n constructor to its registry-qualified name, so a same-named class elsewhere\n never collides; a raw simple name two classes share fails closed.\n- Treat list/get tools as read-only; mutating tools require authentication unless the app deliberately wraps them with stronger policy.\n- Keep app policy outside generated tool schemas. The wrapper owns auth, allow-list, and workflow assertion behavior.\n- Return 404 for tools outside the app allow-list so private generated tools are not enumerated by mistake.\n- Trust only a principal supplied by the application adapter. This package\n does not implement an OAuth authorization server. The opt-in `./auth` entry\n validates JWT access tokens and requires an application-owned principal mapper.\n- Mount `mountMcpRoute` as one modern `POST` endpoint. It creates a fresh\n protocol server per request, emits no session id, and refuses subscriptions.\n- `mountMcpAppRoute` (`src/defaults.ts` + `./sveltekit`) is the one-call app\n route: explicit `models` allow-list, required `requiredScopes`, principal from\n session locals (`principalFromSessionLocals`). App policies compose with the\n default and may only narrow it. Its `Origin` check (default on; opt-in on\n `mountMcpRoute`) runs before auth and dispatch; no-`Origin` clients pass.\n `effects` filters the catalog by `mcpToolEffect` (excluded tools 404).\n `auth` swaps in bearer principals; `bindPrincipal` runs their dispatch in\n the app's principal context (RLS transaction), failing closed, and\n authorizes with the binder's narrowed (live) scopes; task lifecycle calls\n re-check the originating action against the allow-list and `effects`,\n after a principal-level `taskPrincipalPolicy` (defaults: scope policy);\n `mountMcpProtectedResourceMetadataRoute` serves only the advertised path.\n- `runtime` (structural `McpAppRouteRuntime`; no app-runtime dependency)\n derives `smrtOptions`, `bindPrincipal` and `auth` on both mounts, the\n profile resolved per request after the origin check (adapters memoized per\n runtime × profile). Fail closed: missing bindings throw at mount; an\n unresolvable profile or missing hosted config rejects (500), never serves\n without the bearer adapter. Explicit options override; keep both forms\n byte-identical (`runtime-route.test.ts`). `./auth` loads lazily from\n `./sveltekit`.\n- `createHostedMcpResourceAuth({ profile, runtime })`: locally, a runtime\n `verifyLocalMcpToken` yields the `sessionFallback` adapter\n (`createLocalMcpTokenAuth`): no `Authorization` header keeps the session\n principal; a presented bearer must verify (401, never cookie fallback); no\n metadata (404). Hosted, `runtime.resolveMcpPrincipal` is the default\n identity mapping; an explicit `resolvePrincipal` overrides it.\n- Adapters stamp `principal.tenantBinding` for `bindPrincipal`: the local\n token adapter always `direct`; the JWT adapter the mapping's value or\n `direct-or-inherited`. Never derive it from request input. Both adapters\n read principals/mappings from own data only (`own-data.ts`): inherited\n values, accessors and poison keys deny.\n- Keep tool catalogs private by default. Public caching requires an explicit\n attestation and a global, unauthenticated, read-only, non-tenant catalog with\n no principal-aware policy.\n\n## Gotchas\n\n- Public tool patterns are still constrained by read-only detection.\n- Policy errors fail closed and expose only the shared safe denial envelope.\n- Workflow assertions run before generated tool dispatch and should be deterministic.\n- Tasks require a stable authenticated principal id, preserve its tenant boundary,\n and require the deployment to run a `TaskRunner` for the `mcp-tasks` queue.\n- This package ships no stdio executable. Use `@happyvertical/smrt-app-cli` to\n bridge a deployed app endpoint to a local MCP client.\n- `mountMcpToolsRoute` and `mountMcpCallRoute` are deprecated REST-shaped\n compatibility aliases, not MCP transports.\n- This package is runtime app infrastructure, not the development MCP. Use `@happyvertical/smrt-dev-mcp` for agentic development knowledge, review, and architecture tooling.\n"
|
|
63
|
+
"agentDoc": "# @happyvertical/smrt-app-mcp\n\nApplication-scoped MCP server wrapper for SMRT apps. The core owns generated\ntool policy; `./sveltekit` owns the stateless Streamable HTTP transport.\n\n## Purpose\n\n- Wraps `@happyvertical/smrt-core` MCP generation with app-level tool allow-lists.\n- Filters unauthenticated tool visibility to public read-only tools.\n- Applies an optional principal-aware policy consistently to discovery and calls.\n- Provides workflow assertions so app servers can reject unsafe or incomplete tool calls before dispatch.\n- Exposes an opt-in durable Tasks extension through `@happyvertical/smrt-jobs`.\n\n## Patterns\n\n- Generate tools from core scoped to the allow-list (`MCPConfig.classNames`),\n then filter by allowed class names: a duplicate tool name among registered\n classes the app never publishes must not fail its catalog or calls (#3490).\n `mountMcpAppRoute`/`createDefaultMcpAppServer` resolve each model\n constructor to its registry-qualified name, so a same-named class elsewhere\n never collides; a raw simple name two classes share fails closed.\n- Treat list/get tools as read-only; mutating tools require authentication unless the app deliberately wraps them with stronger policy.\n- Keep app policy outside generated tool schemas. The wrapper owns auth, allow-list, and workflow assertion behavior.\n- Return 404 for tools outside the app allow-list so private generated tools are not enumerated by mistake.\n- Trust only a principal supplied by the application adapter. This package\n does not implement an OAuth authorization server. The opt-in `./auth` entry\n validates JWT access tokens and requires an application-owned principal mapper.\n- Mount `mountMcpRoute` as one modern `POST` endpoint. It creates a fresh\n protocol server per request, emits no session id, and refuses subscriptions.\n- `mountMcpAppRoute` (`src/defaults.ts` + `./sveltekit`) is the one-call app\n route: explicit `models` allow-list, required `requiredScopes`, principal from\n session locals (`principalFromSessionLocals`). App policies compose with the\n default and may only narrow it. Its `Origin` check (default on; opt-in on\n `mountMcpRoute`) runs before auth and dispatch; no-`Origin` clients pass.\n `effects` filters the catalog by `mcpToolEffect` (excluded tools 404).\n `auth` swaps in bearer principals; `bindPrincipal` runs their dispatch in\n the app's principal context (RLS transaction), failing closed, and\n authorizes with the binder's narrowed (live) scopes; task lifecycle calls\n re-check the originating action against the allow-list and `effects`,\n after a principal-level `taskPrincipalPolicy` (defaults: scope policy);\n `mountMcpProtectedResourceMetadataRoute` serves only the advertised path.\n- `runtime` (structural `McpAppRouteRuntime`; no app-runtime dependency)\n derives `smrtOptions`, `bindPrincipal` and `auth` on both mounts, the\n profile resolved per request after the origin check (adapters memoized per\n runtime × profile). Fail closed: missing bindings throw at mount; an\n unresolvable profile or missing hosted config rejects (500), never serves\n without the bearer adapter. Explicit options override; keep both forms\n byte-identical (`runtime-route.test.ts`). `./auth` loads lazily from\n `./sveltekit`.\n- `createHostedMcpResourceAuth({ profile, runtime })`: locally, a runtime\n `verifyLocalMcpToken` yields the `sessionFallback` adapter\n (`createLocalMcpTokenAuth`): no `Authorization` header keeps the session\n principal; a presented bearer must verify (401, never cookie fallback); no\n metadata (404). Hosted, `runtime.resolveMcpPrincipal` is the default\n identity mapping; an explicit `resolvePrincipal` overrides it.\n- Adapters stamp `principal.tenantBinding` for `bindPrincipal`: the local\n token adapter always `direct`; the JWT adapter the mapping's value or\n `direct-or-inherited`. Never derive it from request input. Both adapters\n read principals/mappings from own data only (`own-data.ts`): inherited\n values, accessors and poison keys deny.\n- Keep tool catalogs private by default. Public caching requires an explicit\n attestation and a global, unauthenticated, read-only, non-tenant catalog with\n no principal-aware policy.\n\n- Host operations (`./host-operations`, browser-safe) are authored once and\n projected by `toMcpWorkflowTool`/`toWebMcpSpec`. `onToolCall` audits every\n call once; its principal/identity come from the adapter, and a failing hook\n never alters a result. Only `McpToolError`/`mapError` output reaches agents.\n Details: README \"Host operations, audit and typed errors\".\n\n## Gotchas\n\n- Public tool patterns are still constrained by read-only detection.\n- Policy errors fail closed and expose only the shared safe denial envelope.\n- Workflow assertions run before generated tool dispatch and should be deterministic.\n- Tasks require a stable authenticated principal id, preserve its tenant boundary,\n and require the deployment to run a `TaskRunner` for the `mcp-tasks` queue.\n- This package ships no stdio executable. Use `@happyvertical/smrt-app-cli` to\n bridge a deployed app endpoint to a local MCP client.\n- `mountMcpToolsRoute` and `mountMcpCallRoute` are deprecated REST-shaped\n compatibility aliases, not MCP transports.\n- This package is runtime app infrastructure, not the development MCP. Use `@happyvertical/smrt-dev-mcp` for agentic development knowledge, review, and architecture tooling.\n"
|
|
63
64
|
}
|
package/dist/sveltekit.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sveltekit.d.ts","sourceRoot":"","sources":["../src/sveltekit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAMjE,OAAO,KAAK,EACV,oBAAoB,EACpB,sBAAsB,EACtB,mBAAmB,EACnB,eAAe,EACf,mBAAmB,EACpB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,KAAK,gCAAgC,EAEtC,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"sveltekit.d.ts","sourceRoot":"","sources":["../src/sveltekit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAMjE,OAAO,KAAK,EACV,oBAAoB,EACpB,sBAAsB,EACtB,mBAAmB,EACnB,eAAe,EACf,mBAAmB,EACpB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,KAAK,gCAAgC,EAEtC,MAAM,eAAe,CAAC;AAOvB,OAAO,EAGL,KAAK,yBAAyB,EAC/B,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAEV,eAAe,EACf,YAAY,EACZ,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAErB,oEAAoE;AACpE,KAAK,qBAAqB,GAAG;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,GAAG,CAAC;CACV,CAAC;AAEF,6EAA6E;AAC7E,MAAM,MAAM,mBAAmB,GAAG,CAChC,KAAK,EAAE,qBAAqB,KACzB,OAAO,CAAC,QAAQ,CAAC,CAAC;AAQvB,8EAA8E;AAC9E,MAAM,MAAM,oBAAoB,GAAG,CACjC,KAAK,EAAE,qBAAqB,KACzB,eAAe,GAAG,IAAI,GAAG,SAAS,CAAC;AAExC,6EAA6E;AAC7E,MAAM,MAAM,eAAe,GAAG,oBAAoB,CAAC;AAwCnD;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,IAAI,CACrC,eAAe,EACf,aAAa,GAAG,kBAAkB,GAAG,cAAc,GAAG,iBAAiB,CACxE,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,0BAA0B,GAClC,oBAAoB,GACpB,IAAI,GACJ,SAAS,GACT,CAAC,MAAM,oBAAoB,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AASpD;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,qEAAqE;IACrE,eAAe,IAAI,OAAO,CAAC;QAAE,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAA;KAAE,CAAC,CAAC;IACvE;;;OAGG;IACH,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAAC;IAC3E;;;OAGG;IACH,mBAAmB,CACjB,QAAQ,EAAE,mBAAmB,GAC5B,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;CACxC;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAmB,SAAQ,mBAAmB;IAC7D,+DAA+D;IAC/D,cAAc,IAAI,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACzC;;;;OAIG;IACH,cAAc,CAAC,CAAC,EACd,SAAS,EAAE,eAAe,GAAG;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,EAC3C,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,iBAAiB,KAAK,OAAO,CAAC,CAAC,CAAC,GAC7C,OAAO,CAAC,CAAC,CAAC,CAAC;CACf;AA+KD;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC,GAAG,eAAe,GAAG,IAAI,CAoBzB;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,EACjC,SAAS,EAAE,eAAe,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EAC3C,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,iBAAiB,KAAK,OAAO,CAAC,CAAC,CAAC,KAC3C,OAAO,CAAC,CAAC,CAAC,CAAC;AAEhB;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,eAAe,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG;IAC1E,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B,CAAC;AAkCF,2CAA2C;AAC3C,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,kBAAkB,CAAC;IACnC;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC;;;;;;;;;;;OAWG;IACH,IAAI,CAAC,EAAE,0BAA0B,CAAC;IAClC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,uFAAuF;IACvF,UAAU,CAAC,EAAE,yBAAyB,CAAC,YAAY,CAAC,CAAC;IACrD;;;OAGG;IACH,gBAAgB,CAAC,EAAE,oBAAoB,CAAC;IACxC;;OAEG;IACH,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,OAAO,CAAC;CAClE;AAkBD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,oBAAyB,GACjC,mBAAmB,CAIrB;AA4HD,4CAA4C;AAC5C,MAAM,WAAW,uBACf,SAAQ,gCAAgC,EACtC,IAAI,CACF,oBAAoB,EAClB,MAAM,GACN,eAAe,GACf,YAAY,GACZ,aAAa,GACb,gBAAgB,GAChB,gBAAgB,CACnB;IACH;;;OAGG;IACH,gBAAgB,CAAC,EAAE,oBAAoB,CAAC;IACxC;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,EAAE,kBAAkB,CAAC;CAC9B;AAED;;;GAGG;AACH,MAAM,WAAW,8BACf,SAAQ,IAAI,CAAC,uBAAuB,EAAE,SAAS,GAAG,aAAa,CAAC;IAChE,OAAO,EAAE,kBAAkB,CAAC;IAC5B,2DAA2D;IAC3D,WAAW,CAAC,EAAE,mBAAmB,CAAC;CACnC;AAED,kEAAkE;AAClE,MAAM,MAAM,sBAAsB,GAAG,mBAAmB,GAAG;IACzD,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;CAC/B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,uBAAuB,GAAG,8BAA8B,GAChE,sBAAsB,CA8CxB;AAED,sEAAsE;AACtE,MAAM,WAAW,6CAA6C;IAC5D,gEAAgE;IAChE,OAAO,EAAE,mBAAmB,CAAC;IAC7B,6EAA6E;IAC7E,IAAI,CAAC,EAAE,0BAA0B,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,sCAAsC,CACpD,MAAM,EACF,0BAA0B,GAC1B,6CAA6C,GAChD,mBAAmB,CA2CrB;AA6OD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,oBAAyB,GACjC,mBAAmB,CAcrB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,oBAAyB,GACjC,mBAAmB,CA0BrB;AAyBD,YAAY,EACV,gCAAgC,EAChC,WAAW,GACZ,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/sveltekit.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as MCP_ORIGIN_DENIED_CODE, o as McpAccessError, r as MCP_TOOL_ACCESS_DENIED_CODE } from "./chunks/errors-XG_zn1pP.js";
|
|
2
|
+
import { a as createDefaultMcpAppServer, r as createMcpProtocolServerForRequest, t as MCP_TASKS_EXTENSION } from "./chunks/protocol-BRQji5k_.js";
|
|
3
|
+
import { t as stampVerifiedIdentity } from "./chunks/identity-BNzyRsWf.js";
|
|
2
4
|
import { classifyInboundRequest, createMcpHandler, isJsonContentType } from "@modelcontextprotocol/server";
|
|
3
5
|
//#region src/sveltekit.ts
|
|
4
6
|
var defaultResolvePrincipal = (event) => event.locals?.user ?? null;
|
|
@@ -143,10 +145,10 @@ function effectiveBoundPrincipal(authenticated, bound) {
|
|
|
143
145
|
if (bound === void 0) return authenticated;
|
|
144
146
|
if (!bound || typeof bound !== "object" || bound.id !== authenticated.id || (bound.tenantId ?? null) !== (authenticated.tenantId ?? null) || !Array.isArray(bound.scopes) || !bound.scopes.every((scope) => typeof scope === "string")) return;
|
|
145
147
|
const granted = new Set(bound.scopes);
|
|
146
|
-
return {
|
|
148
|
+
return stampVerifiedIdentity({
|
|
147
149
|
...authenticated,
|
|
148
150
|
scopes: (authenticated.scopes ?? []).filter((scope) => granted.has(scope))
|
|
149
|
-
};
|
|
151
|
+
}, authenticated.identity);
|
|
150
152
|
}
|
|
151
153
|
function protocolServerForRequest(server, resolved) {
|
|
152
154
|
if (!resolved.legacyAuthenticated || resolved.principal) return server;
|
package/dist/sveltekit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sveltekit.js","names":[],"sources":["../src/sveltekit.ts"],"sourcesContent":["/**\n * SvelteKit route adapters for an `McpAppServer`. Mirrors the\n * `@happyvertical/smrt-users/sveltekit` pattern: minimal `HandleInput` type\n * so we never need `@sveltejs/kit` as a real dependency.\n *\n * @packageDocumentation\n *\n * @example\n * ```ts\n * // src/routes/api/mcp/+server.ts — one call with the app defaults\n * import { mountMcpAppRoute } from '@happyvertical/smrt-app-mcp/sveltekit';\n * import { Item } from '$lib/objects/Item';\n * import { runtime } from '$lib/server/smrt';\n * export const POST = mountMcpAppRoute({\n * runtime,\n * models: [Item],\n * requiredScopes: ['items.read'],\n * effects: ['read'],\n * });\n * ```\n *\n * @example\n * ```ts\n * // src/routes/api/mcp/+server.ts — a custom server core\n * import { mountMcpRoute } from '@happyvertical/smrt-app-mcp/sveltekit';\n * import { mcpServer } from '$lib/server/mcp';\n * export const POST = mountMcpRoute(mcpServer);\n * ```\n */\n\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport {\n classifyInboundRequest,\n createMcpHandler,\n isJsonContentType,\n} from '@modelcontextprotocol/server';\nimport type {\n McpDeploymentProfile,\n McpLocalTokenPrincipal,\n McpPrincipalMapping,\n McpResourceAuth,\n McpVerifiedIdentity,\n} from './auth.js';\nimport {\n type CreateDefaultMcpAppServerOptions,\n createDefaultMcpAppServer,\n} from './defaults.js';\nimport {\n MCP_ORIGIN_DENIED_CODE,\n MCP_TOOL_ACCESS_DENIED_CODE,\n McpAccessError,\n} from './errors.js';\nimport {\n createMcpProtocolServerForRequest,\n MCP_TASKS_EXTENSION,\n type McpProtocolRequestOptions,\n} from './protocol.js';\nimport type {\n CallToolInput,\n McpAppPrincipal,\n McpAppServer,\n McpSmrtOptionsThunk,\n} from './server.js';\n\n/** Minimal subset of a SvelteKit RequestEvent we actually touch. */\ntype SvelteKitRequestEvent = {\n locals?: Record<string, unknown>;\n request: Request;\n url: URL;\n};\n\n/** A SvelteKit `+server.ts` request handler with no SvelteKit dependency. */\nexport type McpSvelteKitHandler = (\n event: SvelteKitRequestEvent,\n) => Promise<Response>;\n\ntype ResolvedRequestPrincipal = {\n principal: McpAppPrincipal | null;\n /** Defined only for the legacy discovery-only authentication adapter. */\n legacyAuthenticated?: boolean;\n};\n\n/** Locals reader used to pull the request principal out of `event.locals`. */\nexport type McpPrincipalResolver = (\n event: SvelteKitRequestEvent,\n) => McpAppPrincipal | null | undefined;\n\n/** Backwards-compatible alias for callers that name the principal a user. */\nexport type McpUserResolver = McpPrincipalResolver;\n\nconst defaultResolvePrincipal: McpPrincipalResolver = (event) =>\n (event.locals?.user ?? null) as McpAppPrincipal | null;\n\nfunction resolveRequestPrincipal(\n event: SvelteKitRequestEvent,\n options: MountMcpRouteOptions,\n): ResolvedRequestPrincipal {\n const resolvePrincipal =\n options.resolvePrincipal ?? options.resolveUser ?? defaultResolvePrincipal;\n const principal = resolvePrincipal(event) ?? null;\n // Keep the legacy boolean gate for existing apps, but apply it to the same\n // principal that both routes receive. A new principal resolver supersedes it.\n if (\n !options.resolvePrincipal &&\n options.resolveAuthenticated &&\n !options.resolveAuthenticated(event)\n ) {\n return { principal: null, legacyAuthenticated: false };\n }\n return {\n principal,\n ...(options.resolvePrincipal || !options.resolveAuthenticated\n ? {}\n : { legacyAuthenticated: true }),\n };\n}\n\nfunction listToolsInput(resolved: ResolvedRequestPrincipal) {\n // Before principal-aware routes, `resolveAuthenticated` affected discovery\n // independently of `resolveUser`. Preserve a true legacy result only when\n // there is no principal to pass; new routes should use `resolvePrincipal`\n // for a single identity on both discovery and calls.\n if (!resolved.principal && resolved.legacyAuthenticated) {\n return { authenticated: true };\n }\n return { principal: resolved.principal };\n}\n\n/**\n * The bearer-authentication surface `mountMcpRoute` and the metadata route\n * need from `createMcpResourceAuth` (`./auth`). Typed structurally so this\n * entry does not load the JWT verifier.\n */\nexport type McpRouteResourceAuth = Pick<\n McpResourceAuth,\n 'metadataUrl' | 'metadataResponse' | 'authenticate' | 'sessionFallback'\n>;\n\n/**\n * A protected-resource adapter, or a thunk read on every request. A `null` or\n * `undefined` result (for example the `local` profile) means the route uses\n * its session principal resolver instead.\n */\nexport type McpRouteResourceAuthSource =\n | McpRouteResourceAuth\n | null\n | undefined\n | (() => McpRouteResourceAuth | null | undefined);\n\nfunction currentResourceAuth(\n source: McpRouteResourceAuthSource,\n): McpRouteResourceAuth | null {\n const value = typeof source === 'function' ? source() : source;\n return value ?? null;\n}\n\n/**\n * The runtime calls the protected-resource metadata route (and the bearer\n * half of {@link mountMcpAppRoute}) use: the deployment profile and the two\n * credential bindings `createHostedMcpResourceAuth` (`./auth`) takes. The\n * runtime from `@happyvertical/smrt-app-runtime/sveltekit` satisfies it as is.\n */\nexport interface McpRouteAuthRuntime {\n /** Resolve the deployment profile (the SMRT runtime memoizes it). */\n resolvedRuntime(): Promise<{ readonly profile: McpDeploymentProfile }>;\n /**\n * Verify an owner-minted local MCP token (`smrt app token`); `null` denies.\n * Consulted only in the `local` profile.\n */\n verifyLocalMcpToken(token: string): Promise<McpLocalTokenPrincipal | null>;\n /**\n * Map a verified hosted access-token identity to the current account and\n * tenant; `null` denies. Consulted only in hosted profiles.\n */\n resolveMcpPrincipal(\n identity: McpVerifiedIdentity,\n ): Promise<McpPrincipalMapping | null>;\n}\n\n/**\n * The runtime calls {@link mountMcpAppRoute} uses; the runtime from\n * `@happyvertical/smrt-app-runtime/sveltekit` satisfies it as is.\n */\nexport interface McpAppRouteRuntime extends McpRouteAuthRuntime {\n /** The request's database (its RLS transaction inside one). */\n databaseConfig(): SmrtClassOptions['db'];\n /**\n * Run `run` bound to a bearer-authenticated principal (see\n * {@link McpPrincipalBinder}): under `database-rls` in a fresh transaction\n * publishing that user, tenant and live permissions capped to its scopes.\n */\n runAsPrincipal<T>(\n principal: McpAppPrincipal & { id: string },\n run: (bound?: McpBoundPrincipal) => Promise<T>,\n ): Promise<T>;\n}\n\nconst ROUTE_AUTH_RUNTIME_METHODS = [\n 'resolvedRuntime',\n 'verifyLocalMcpToken',\n 'resolveMcpPrincipal',\n] as const;\nconst APP_ROUTE_RUNTIME_METHODS = [\n ...ROUTE_AUTH_RUNTIME_METHODS,\n 'databaseConfig',\n 'runAsPrincipal',\n] as const;\nconst DEPLOYMENT_PROFILES: readonly string[] = [\n 'local',\n 'self-hosted',\n 'cloud',\n];\n\n/** Fail closed at mount: a runtime missing any binding is a wiring error. */\nfunction assertRuntime(\n runtime: unknown,\n methods: readonly string[],\n mount: string,\n): void {\n const value = runtime as Record<string, unknown> | null;\n const missing =\n !value || typeof value !== 'object'\n ? methods\n : methods.filter((method) => typeof value[method] !== 'function');\n if (missing.length > 0) {\n throw new TypeError(\n `${mount}: runtime must provide ${missing.join(', ')}.`,\n );\n }\n}\n\n/**\n * One `createHostedMcpResourceAuth` source per runtime and profile, shared by\n * the app route and the metadata route like the module an app used to keep.\n * Keyed by profile, so a runtime whose profile changes never reuses another\n * profile's adapter; each source keeps its own caching and retry rules.\n */\nconst runtimeAuthSources = new WeakMap<\n object,\n Map<McpDeploymentProfile, () => McpRouteResourceAuth | null>\n>();\nlet authModule: Promise<typeof import('./auth.js')> | undefined;\n\n/** Load `./auth` (the JWT verifier) only when a runtime-derived route needs it. */\nfunction loadAuthModule(): Promise<typeof import('./auth.js')> {\n authModule ??= import('./auth.js').catch((error: unknown) => {\n authModule = undefined;\n throw error;\n });\n return authModule;\n}\n\n/**\n * The bearer adapter a runtime provides for its profile, resolved once per\n * request. Rejects (the route answers 500, before any dispatch) when the\n * profile cannot be resolved or is unknown, when hosted configuration is\n * missing, or when no adapter results: a runtime route never serves a\n * request without its profile's bearer adapter (which would ignore a\n * presented bearer and leave only the session principal).\n */\nasync function runtimeResourceAuth(\n runtime: McpRouteAuthRuntime,\n): Promise<McpRouteResourceAuth> {\n const resolved = await runtime.resolvedRuntime();\n const profile = (resolved as { profile?: unknown } | null | undefined)\n ?.profile;\n if (typeof profile !== 'string' || !DEPLOYMENT_PROFILES.includes(profile)) {\n throw new Error('MCP runtime resolved an unknown deployment profile.');\n }\n let sources = runtimeAuthSources.get(runtime);\n if (!sources) {\n sources = new Map();\n runtimeAuthSources.set(runtime, sources);\n }\n let source = sources.get(profile as McpDeploymentProfile);\n if (!source) {\n const { createHostedMcpResourceAuth } = await loadAuthModule();\n source =\n sources.get(profile as McpDeploymentProfile) ??\n createHostedMcpResourceAuth({\n profile: profile as McpDeploymentProfile,\n runtime,\n });\n sources.set(profile as McpDeploymentProfile, source);\n }\n const adapter = source();\n if (!adapter) {\n throw new Error('MCP runtime provided no bearer adapter for its profile.');\n }\n return adapter;\n}\n\n/** Parse an origin to its canonical `scheme://host[:port]`, or `undefined`. */\nfunction canonicalOrigin(value: string): string | undefined {\n if (value === 'null') return undefined;\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return undefined;\n }\n if (url.protocol !== 'https:' && url.protocol !== 'http:') return undefined;\n if (url.username || url.password || url.search || url.hash) return undefined;\n if (url.pathname !== '/') return undefined;\n return url.origin;\n}\n\nfunction normalizeTrustedOrigins(\n values: readonly string[],\n): ReadonlySet<string> {\n if (!Array.isArray(values)) {\n throw new TypeError('MCP trustedOrigins must be an array of origins.');\n }\n const origins = new Set<string>();\n for (const value of values) {\n const origin =\n typeof value === 'string' ? canonicalOrigin(value) : undefined;\n if (!origin) throw new TypeError('Invalid MCP trusted origin.');\n origins.add(origin);\n }\n return origins;\n}\n\n/**\n * Same-origin is judged against `event.url`, which SvelteKit derives from the\n * adapter's configured origin (for example adapter-node `ORIGIN`, or its\n * `PROTOCOL_HEADER`/`HOST_HEADER` proxy settings). This package does not read\n * `X-Forwarded-*` itself.\n */\nfunction originPermitted(\n event: SvelteKitRequestEvent,\n trusted: ReadonlySet<string>,\n): boolean {\n const header = event.request.headers.get('origin');\n if (header !== null) {\n const origin = canonicalOrigin(header.trim());\n return (\n origin !== undefined &&\n (origin === event.url.origin || trusted.has(origin))\n );\n }\n // No Origin: a non-browser client, unless fetch metadata says otherwise.\n const site = event.request.headers\n .get('sec-fetch-site')\n ?.trim()\n .toLowerCase();\n return site !== 'cross-site' && site !== 'same-site';\n}\n\nfunction originDeniedResponse(): Response {\n return new Response(\n JSON.stringify({\n jsonrpc: '2.0',\n id: null,\n error: {\n code: -32600,\n message: 'MCP request origin is not permitted.',\n data: { code: MCP_ORIGIN_DENIED_CODE, retryable: false },\n },\n }),\n {\n status: 403,\n headers: {\n 'content-type': 'application/json',\n 'cache-control': 'no-store',\n },\n },\n );\n}\n\n/**\n * Project the session hook's request-local result onto an MCP principal.\n *\n * Reads only `locals.user.id`, the session-authorized `locals.tenantId`, and\n * `locals.permissions` as populated by `createSessionHandler` from\n * `@happyvertical/smrt-users/sveltekit`. Headers, URL-selected tenants\n * (`locals.selectedTenantId`), request bodies and route arguments are never\n * identity inputs. Any missing or malformed field yields `null`\n * (unauthenticated), which fails closed for every non-public tool.\n */\nexport function principalFromSessionLocals(event: {\n locals?: Record<string, unknown>;\n}): McpAppPrincipal | null {\n const locals = event.locals;\n if (!locals || typeof locals !== 'object') return null;\n const user = locals.user as { id?: unknown } | null | undefined;\n const tenantId = locals.tenantId;\n const permissions = locals.permissions;\n if (!user || typeof user !== 'object') return null;\n if (typeof user.id !== 'string' || user.id.length === 0) return null;\n if (typeof tenantId !== 'string' || tenantId.length === 0) return null;\n if (\n !Array.isArray(permissions) ||\n !permissions.every((value) => typeof value === 'string')\n )\n return null;\n return {\n id: user.id,\n tenantId,\n kind: 'human',\n scopes: [...permissions].sort(),\n };\n}\n\n/**\n * Run MCP dispatch for a bearer-authenticated principal inside an\n * application-owned context bound to that principal (for example\n * `runtime.runAsPrincipal` from `@happyvertical/smrt-app-runtime/sveltekit`,\n * which opens the principal's permission context and, under `database-rls`,\n * its RLS transaction). Must call `run` exactly once and return its result;\n * throwing before `run` (an unbindable principal) is a denial.\n *\n * Pass `run` the bound principal carrying its *effective* `scopes` (for\n * example the token scopes still granted by live membership permissions).\n * Dispatch then authorizes with those scopes, intersected with the token's:\n * a binder can only narrow authority, and changing the principal's `id` or\n * `tenantId` is a denial. Calling `run()` with no argument keeps the token\n * scopes.\n */\nexport type McpPrincipalBinder = <T>(\n principal: McpAppPrincipal & { id: string },\n run: (bound?: McpBoundPrincipal) => Promise<T>,\n) => Promise<T>;\n\n/**\n * The principal a binder hands back to `run`: the authenticated identity,\n * possibly as a frozen snapshot whose arrays are readonly (for example the\n * deep-frozen principal `runtime.runAsPrincipal` passes its callback).\n */\nexport type McpBoundPrincipal = Omit<McpAppPrincipal, 'scopes' | 'roles'> & {\n id: string;\n scopes?: readonly string[];\n roles?: readonly string[];\n};\n\n/**\n * The principal dispatch authorizes after binding: the authenticated identity\n * with only the scopes both the token and the binder grant. `undefined` when\n * the binder changed the identity or returned malformed scopes.\n */\nfunction effectiveBoundPrincipal(\n authenticated: McpAppPrincipal & { id: string },\n bound: McpBoundPrincipal | undefined,\n): (McpAppPrincipal & { id: string }) | undefined {\n if (bound === undefined) return authenticated;\n if (\n !bound ||\n typeof bound !== 'object' ||\n bound.id !== authenticated.id ||\n (bound.tenantId ?? null) !== (authenticated.tenantId ?? null) ||\n !Array.isArray(bound.scopes) ||\n !bound.scopes.every((scope) => typeof scope === 'string')\n ) {\n return undefined;\n }\n const granted = new Set(bound.scopes);\n return {\n ...authenticated,\n scopes: (authenticated.scopes ?? []).filter((scope) => granted.has(scope)),\n };\n}\n\n/** Options shared by both route mounts. */\nexport interface MountMcpRouteOptions {\n /**\n * Bind a bearer-authenticated principal around dispatch (see\n * {@link McpPrincipalBinder}). The `smrtOptions` database thunk, task\n * handling and tool execution all run inside the binding, and the response\n * is fully materialized before it returns. A binder that throws before\n * running dispatch, or returns without running it, yields HTTP 403 with the\n * safe `mcp_tool_access_denied` JSON-RPC error. Scopes the binder hands\n * back become the effective authority for the tool policy, so live\n * revocation applies even without a database-enforced policy. Used only when `auth`\n * authenticated the request; the session-locals path is unchanged.\n */\n bindPrincipal?: McpPrincipalBinder;\n /**\n * Refuse a modern-endpoint request whose `Origin` header is present but is\n * neither the request URL's own origin nor in `trustedOrigins` (and, when\n * `Origin` is absent, one whose `Sec-Fetch-Site` is `cross-site` or\n * `same-site`). The check runs before bearer authentication, principal\n * resolution and any task or tool dispatch, and answers 403 with the safe\n * `mcp_origin_denied` JSON-RPC error. Requests without browser origin\n * signals (server-side MCP clients, the `smrt-app-cli` stdio bridge) are\n * unaffected. Off unless set for `mountMcpRoute`; on by default for\n * `mountMcpAppRoute`. Ignored by the deprecated REST-shaped mounts.\n */\n checkOrigin?: boolean;\n /**\n * Additional exact origins (`scheme://host[:port]`) accepted by the origin\n * check, e.g. a browser-hosted MCP client, or the public origin when a\n * proxy makes `event.url` differ from what browsers send. `null`, wildcards,\n * paths and credentials are rejected at mount time.\n */\n trustedOrigins?: readonly string[];\n /**\n * Optional bearer authentication for the modern `mountMcpRoute` endpoint.\n * When the source yields an adapter, every request must carry a valid\n * bearer token: a failed check returns the adapter's challenge response\n * before any dispatch, and the adapter's mapped principal replaces the\n * session principal (`resolvePrincipal` is not consulted). When it yields\n * `null`, the route resolves its principal from the request as usual. A\n * `sessionFallback` adapter (the local owner-token adapter) challenges only\n * requests that carry an `Authorization` header; others keep the session\n * principal. Set `allowAnonymous` to permit a headerless request with a\n * non-fallback adapter. Ignored by the deprecated REST-shaped mounts.\n */\n auth?: McpRouteResourceAuthSource;\n /**\n * Permit an anonymous request when an auth adapter is configured but no\n * Authorization header is present. The server policy still exposes only\n * explicitly public read-only tools and resources. A presented bearer is\n * always verified; a bad token never falls back to anonymous access.\n */\n allowAnonymous?: boolean;\n /** Optional extension discovery projected from the request-authorized tool catalog. */\n extensions?: McpProtocolRequestOptions['extensions'];\n /**\n * Resolve the request principal once for both discovery and direct calls.\n * Defaults to `event.locals.user`.\n */\n resolvePrincipal?: McpPrincipalResolver;\n /**\n * Backwards-compatible alias for `resolvePrincipal`.\n */\n resolveUser?: McpUserResolver;\n /**\n * Deprecated legacy authentication gate. When `resolvePrincipal` is not\n * supplied, a false result makes the principal null for both routes.\n */\n resolveAuthenticated?: (event: SvelteKitRequestEvent) => boolean;\n}\n\nfunction protocolServerForRequest(\n server: McpAppServer,\n resolved: ResolvedRequestPrincipal,\n): McpAppServer {\n // Older applications sometimes used a boolean discovery-only adapter. Keep\n // its positive result intact for the deprecated resolver while new mounts\n // consistently use the principal on both MCP methods.\n if (!resolved.legacyAuthenticated || resolved.principal) return server;\n return {\n ...server,\n serverInfo: server.serverInfo,\n listTools: () => server.listTools({ authenticated: true }),\n callTool: (input) => server.callTool(input),\n };\n}\n\n/**\n * Mount a modern, stateless Streamable HTTP MCP endpoint as a SvelteKit\n * `POST` handler. The scoped SDK validates the 2026-07-28 envelope plus the\n * required `Mcp-Method` and `Mcp-Name` headers, returning `-32020` on a\n * mismatch. A fresh protocol server is created for each HTTP request, so this\n * route holds neither MCP sessions nor request principal state between nodes.\n */\nexport function mountMcpRoute(\n server: McpAppServer,\n options: MountMcpRouteOptions = {},\n): McpSvelteKitHandler {\n return mountMcpRouteWith(server, options, () =>\n currentResourceAuth(options.auth),\n );\n}\n\n/**\n * {@link mountMcpRoute} with the bearer adapter supplied per request by\n * `resolveAuth` (sync for an `auth` source, async for a runtime). It runs\n * after the origin check and before any principal or dispatch work; a throw\n * or rejection rejects the handler (no dispatch).\n */\nfunction mountMcpRouteWith(\n server: McpAppServer,\n options: MountMcpRouteOptions,\n resolveAuth: () =>\n | McpRouteResourceAuth\n | null\n | Promise<McpRouteResourceAuth | null>,\n): McpSvelteKitHandler {\n const trustedOrigins = options.checkOrigin\n ? normalizeTrustedOrigins(options.trustedOrigins ?? [])\n : undefined;\n return async (event) => {\n // Ambient browser credentials (session cookies) must not let another\n // origin drive this endpoint; refuse before any identity or dispatch work.\n if (trustedOrigins && !originPermitted(event, trustedOrigins)) {\n return originDeniedResponse();\n }\n const auth = await resolveAuth();\n if (!auth) return dispatch(event, resolveRequestPrincipal(event, options));\n if (!event.request.headers.has('authorization')) {\n // A session-fallback adapter (local owner tokens) keeps its established\n // session path. Explicit anonymous access is separate: it deliberately\n // ignores session locals and custom resolvers, so bearer auth cannot be\n // bypassed through ambient authority.\n if (auth.sessionFallback === true) {\n return dispatch(event, resolveRequestPrincipal(event, options));\n }\n if (options.allowAnonymous === true) {\n return dispatch(event, { principal: null });\n }\n }\n const checked = await auth.authenticate(event.request);\n if (!checked.ok) return checked.response;\n if (!options.bindPrincipal) {\n return dispatch(event, { principal: checked.principal });\n }\n let entered = false;\n let response: Response;\n try {\n response = await options.bindPrincipal(\n checked.principal,\n async (bound) => {\n entered = true;\n const principal = effectiveBoundPrincipal(checked.principal, bound);\n if (!principal) return principalUnboundResponse();\n // Materialize the body inside the binding so no dispatch work can\n // outlive the principal's context (or its RLS transaction).\n const dispatched = await dispatch(event, { principal });\n return new Response(await dispatched.arrayBuffer(), {\n status: dispatched.status,\n statusText: dispatched.statusText,\n headers: dispatched.headers,\n });\n },\n );\n } catch (error) {\n if (entered) throw error;\n return principalUnboundResponse();\n }\n return entered ? response : principalUnboundResponse();\n };\n\n async function dispatch(\n event: SvelteKitRequestEvent,\n resolved: ResolvedRequestPrincipal,\n ): Promise<Response> {\n const taskResponse = await maybeHandleTaskRequest(\n server,\n resolved.principal,\n event.request,\n );\n if (taskResponse) return taskResponse;\n const handler = createMcpHandler(\n () =>\n createMcpProtocolServerForRequest(\n protocolServerForRequest(server, resolved),\n {\n principal: resolved.principal,\n extensions: options.extensions,\n },\n ),\n {\n // The legacy REST-shaped mounts below remain a deprecated migration\n // path. This endpoint accepts only the modern MCP protocol.\n legacy: 'reject',\n // The SDK validates every request before consulting its listen router.\n // Zero capacity keeps this mount stateless by refusing a\n // listen request before it can open an SSE response.\n maxSubscriptions: 0,\n },\n );\n return handler.fetch(event.request);\n }\n}\n\nfunction principalUnboundResponse(): Response {\n return new Response(\n JSON.stringify({\n jsonrpc: '2.0',\n id: null,\n error: {\n code: -32600,\n message: 'MCP tool access is not permitted.',\n data: { code: MCP_TOOL_ACCESS_DENIED_CODE, retryable: false },\n },\n }),\n {\n status: 403,\n headers: {\n 'content-type': 'application/json',\n 'cache-control': 'no-store',\n },\n },\n );\n}\n\n/** Options for {@link mountMcpAppRoute}. */\nexport interface MountMcpAppRouteOptions\n extends CreateDefaultMcpAppServerOptions,\n Pick<\n MountMcpRouteOptions,\n | 'auth'\n | 'bindPrincipal'\n | 'extensions'\n | 'checkOrigin'\n | 'trustedOrigins'\n | 'allowAnonymous'\n > {\n /**\n * Resolve the request principal when no bearer adapter is active. Defaults\n * to {@link principalFromSessionLocals}.\n */\n resolvePrincipal?: McpPrincipalResolver;\n /**\n * The application runtime (`runtime` from\n * `@happyvertical/smrt-app-runtime/sveltekit`). It supplies the three\n * bindings an app otherwise wires by hand, each still overridable:\n * `smrtOptions` defaults to `() => ({ db: runtime.databaseConfig() })`,\n * `bindPrincipal` to `runtime.runAsPrincipal`, and `auth` to\n * `createHostedMcpResourceAuth({ profile, runtime })` for the profile\n * resolved per request (`runtime.resolvedRuntime()`, after the origin\n * check), so the route module needs no top-level `await`. Fails closed: a\n * runtime missing a binding throws at mount, and a profile that cannot be\n * resolved, or a hosted profile without its configuration, rejects the\n * request (HTTP 500) before dispatch instead of serving it without the\n * profile's bearer adapter. An explicit `auth` (including `null`) is used\n * as given.\n */\n runtime?: McpAppRouteRuntime;\n}\n\n/**\n * {@link MountMcpAppRouteOptions} with a `runtime`, which makes `smrtOptions`\n * optional (it defaults to the runtime's request database).\n */\nexport interface MountMcpAppRuntimeRouteOptions\n extends Omit<MountMcpAppRouteOptions, 'runtime' | 'smrtOptions'> {\n runtime: McpAppRouteRuntime;\n /** Default: `() => ({ db: runtime.databaseConfig() })`. */\n smrtOptions?: McpSmrtOptionsThunk;\n}\n\n/** A mounted app route; `server` is the policy core it serves. */\nexport type McpAppSvelteKitHandler = McpSvelteKitHandler & {\n readonly server: McpAppServer;\n};\n\n/**\n * One-call application MCP endpoint: the app's declared `models`, the default\n * principal scope policy (see `createDefaultMcpAppServer`), principals from\n * the SvelteKit session locals, and optional bearer authentication, mounted\n * as the stateless {@link mountMcpRoute} `POST` endpoint. The origin check\n * (`checkOrigin`) is on by default because the default principal is an\n * ambient session cookie; set `trustedOrigins` for extra browser origins or\n * `checkOrigin: false` to opt out.\n *\n * With `runtime`, the request database, the bearer adapter (local owner\n * tokens or hosted access tokens, per the resolved profile) and the principal\n * binding come from the application runtime (see\n * {@link MountMcpAppRouteOptions.runtime}).\n *\n * @example\n * ```ts\n * // src/routes/api/mcp/+server.ts\n * import { mountMcpAppRoute } from '@happyvertical/smrt-app-mcp/sveltekit';\n * import { Item } from '$lib/objects/Item';\n * import { runtime } from '$lib/server/smrt';\n *\n * export const POST = mountMcpAppRoute({\n * runtime,\n * models: [Item],\n * requiredScopes: ['items.read'],\n * effects: ['read'],\n * });\n * ```\n */\nexport function mountMcpAppRoute(\n options: MountMcpAppRouteOptions | MountMcpAppRuntimeRouteOptions,\n): McpAppSvelteKitHandler {\n const {\n auth,\n allowAnonymous,\n bindPrincipal,\n extensions,\n resolvePrincipal,\n checkOrigin,\n trustedOrigins,\n runtime,\n ...serverOptions\n } = options;\n if (runtime !== undefined) {\n assertRuntime(runtime, APP_ROUTE_RUNTIME_METHODS, 'mountMcpAppRoute');\n }\n const server = createDefaultMcpAppServer({\n ...serverOptions,\n smrtOptions:\n serverOptions.smrtOptions !== undefined || !runtime\n ? (serverOptions.smrtOptions as McpSmrtOptionsThunk)\n : () => ({ db: runtime.databaseConfig() }),\n });\n const routeOptions: MountMcpRouteOptions = {\n auth,\n allowAnonymous,\n bindPrincipal:\n bindPrincipal !== undefined || !runtime\n ? bindPrincipal\n : (principal, run) => runtime.runAsPrincipal(principal, run),\n extensions,\n // Default on: the default principal is an ambient session cookie.\n checkOrigin: checkOrigin ?? true,\n trustedOrigins,\n resolvePrincipal: resolvePrincipal ?? principalFromSessionLocals,\n };\n const handler = mountMcpRouteWith(\n server,\n routeOptions,\n auth !== undefined || !runtime\n ? () => currentResourceAuth(auth)\n : () => runtimeResourceAuth(runtime),\n );\n return Object.defineProperty(handler, 'server', {\n value: server,\n enumerable: true,\n }) as McpAppSvelteKitHandler;\n}\n\n/** Options form of {@link mountMcpProtectedResourceMetadataRoute}. */\nexport interface MountMcpProtectedResourceMetadataRouteOptions {\n /** The application runtime; derives the adapter per request. */\n runtime: McpRouteAuthRuntime;\n /** Explicit adapter source; when set, used as given instead of `runtime`. */\n auth?: McpRouteResourceAuthSource;\n}\n\n/**\n * Mount the RFC 9728 protected-resource metadata document as a SvelteKit\n * `GET` handler, at the exact `metadataUrl` path the bearer challenge\n * advertises (for `/api/mcp`:\n * `src/routes/.well-known/oauth-protected-resource/api/mcp/+server.ts`).\n *\n * Returns 404 when the source yields no adapter or the local owner-token\n * adapter (the `local` profile), or when the request path is not the\n * advertised metadata path. This handler does\n * not implement an OAuth authorization server; the metadata names the\n * operator-owned issuer.\n *\n * Pass `{ runtime }` to derive the adapter the way\n * {@link mountMcpAppRoute} does (the profile resolved per request, the same\n * cached adapter as the app route); an unresolvable profile or missing hosted\n * configuration rejects the request (HTTP 500).\n *\n * @example\n * ```ts\n * // src/routes/.well-known/oauth-protected-resource/api/mcp/+server.ts\n * import { mountMcpProtectedResourceMetadataRoute } from '@happyvertical/smrt-app-mcp/sveltekit';\n * import { runtime } from '$lib/server/smrt';\n * export const GET = mountMcpProtectedResourceMetadataRoute({ runtime });\n * ```\n */\nexport function mountMcpProtectedResourceMetadataRoute(\n source:\n | McpRouteResourceAuthSource\n | MountMcpProtectedResourceMetadataRouteOptions,\n): McpSvelteKitHandler {\n let resolveAuth: () =>\n | McpRouteResourceAuth\n | null\n | Promise<McpRouteResourceAuth | null>;\n if (\n source !== null &&\n typeof source === 'object' &&\n Object.hasOwn(source, 'runtime')\n ) {\n if (Object.hasOwn(source, 'authenticate')) {\n throw new TypeError(\n 'mountMcpProtectedResourceMetadataRoute: pass an auth adapter or { runtime }, not both.',\n );\n }\n const { runtime, auth } =\n source as MountMcpProtectedResourceMetadataRouteOptions;\n assertRuntime(\n runtime,\n ROUTE_AUTH_RUNTIME_METHODS,\n 'mountMcpProtectedResourceMetadataRoute',\n );\n resolveAuth =\n auth !== undefined\n ? () => currentResourceAuth(auth)\n : () => runtimeResourceAuth(runtime);\n } else {\n const auth = source as McpRouteResourceAuthSource;\n resolveAuth = () => currentResourceAuth(auth);\n }\n return async (event) => {\n const current = await resolveAuth();\n if (\n !current ||\n current.sessionFallback === true ||\n event.url.pathname !== new URL(current.metadataUrl).pathname\n )\n return new Response(null, {\n status: 404,\n headers: { 'Cache-Control': 'no-store' },\n });\n return current.metadataResponse();\n };\n}\n\n/**\n * The installed MCP SDK validates tools/call against a pre-Tasks result codec.\n * Intercept the extension before the SDK handler so the regular stateless HTTP\n * protocol stays untouched for every other method.\n */\nasync function maybeHandleTaskRequest(\n server: McpAppServer,\n principal: McpAppPrincipal | null,\n request: Request,\n): Promise<Response | null> {\n if (\n !server.tasksEnabled ||\n !server.callTask ||\n !server.getTask ||\n !server.updateTask ||\n !server.cancelTask ||\n request.method !== 'POST'\n ) {\n return null;\n }\n const body = (await request\n .clone()\n .json()\n .catch(() => null)) as {\n id?: string | number | null;\n jsonrpc?: string;\n method?: string;\n params?: Record<string, unknown>;\n } | null;\n if (body?.jsonrpc !== '2.0' || body.id === undefined) return null;\n const params = body.params ?? {};\n const taskTool =\n body.method === 'tools/call' &&\n typeof params.name === 'string' &&\n (await server.isTaskTool?.(params.name)) === true;\n const taskMethod = ['tasks/get', 'tasks/update', 'tasks/cancel'].includes(\n body.method ?? '',\n );\n if (!taskTool && !taskMethod) return null;\n\n // Keep task requests on the SDK's protocol-validation path until their\n // 2026 request envelope is known-good. In particular, never enqueue durable\n // work for malformed envelopes, unsupported revisions, or non-JSON bodies.\n // The fallback handler owns its wire-exact error response for those cases.\n if (!isJsonContentType(request.headers.get('content-type'))) return null;\n const classification = classifyInboundRequest({\n httpMethod: request.method,\n protocolVersionHeader:\n request.headers.get('mcp-protocol-version') ?? undefined,\n mcpMethodHeader: request.headers.get('mcp-method') ?? undefined,\n mcpNameHeader: request.headers.get('mcp-name') ?? undefined,\n body: body as never,\n });\n if (classification.kind === 'reject') {\n return jsonRpcResponse(\n body.id,\n undefined,\n {\n code: classification.code,\n message: classification.message,\n ...(classification.data === undefined\n ? {}\n : { data: classification.data }),\n },\n classification.httpStatus,\n );\n }\n if (\n classification.kind !== 'modern' ||\n classification.classification.revision !== '2026-07-28'\n ) {\n return null;\n }\n\n // The SDK normally performs this modern HTTP routing validation before\n // dispatch. Task responses are intercepted ahead of that SDK handler, so\n // retain the same fail-closed header/body contract here.\n const headerMismatch = taskHeaderMismatch(request, body.method, params);\n if (headerMismatch) {\n return jsonRpcResponse(body.id, undefined, headerMismatch, 400);\n }\n\n const clientCapabilities = asRecord(params._meta)[\n 'io.modelcontextprotocol/clientCapabilities'\n ];\n const clientSupportsTasks = Object.hasOwn(\n asRecord(asRecord(clientCapabilities).extensions),\n MCP_TASKS_EXTENSION,\n );\n\n // A synchronous fallback exists for tools/call; lifecycle methods do not.\n if (!clientSupportsTasks && body.method === 'tools/call') return null;\n if (!clientSupportsTasks) {\n return jsonRpcResponse(\n body.id,\n undefined,\n {\n code: -32021,\n message: 'Missing required client capability',\n data: {\n requiredCapabilities: { extensions: { [MCP_TASKS_EXTENSION]: {} } },\n },\n },\n 400,\n );\n }\n\n try {\n if (body.method === 'tools/call') {\n const result = await server.callTask({\n name: params.name as string,\n arguments: (params.arguments as Record<string, unknown>) ?? {},\n principal,\n });\n return jsonRpcResponse(body.id, result);\n }\n if (typeof params.taskId !== 'string') {\n return jsonRpcResponse(body.id, undefined, {\n code: -32602,\n message: 'taskId is required',\n });\n }\n if (body.method === 'tasks/get') {\n const task = await server.getTask({ taskId: params.taskId, principal });\n return jsonRpcResponse(body.id, { resultType: 'complete', ...task });\n }\n if (body.method === 'tasks/update') {\n await server.updateTask({\n taskId: params.taskId,\n inputResponses:\n params.inputResponses && typeof params.inputResponses === 'object'\n ? (params.inputResponses as Record<string, unknown>)\n : {},\n principal,\n });\n return jsonRpcResponse(body.id, { resultType: 'complete' });\n }\n await server.cancelTask({ taskId: params.taskId, principal });\n return jsonRpcResponse(body.id, { resultType: 'complete' });\n } catch (error) {\n if (error instanceof McpAccessError) {\n const { code, retryable } = error.metadata;\n return jsonRpcResponse(body.id, undefined, {\n code: error.status === 404 ? -32602 : -32600,\n message: error.message,\n data: {\n ...(typeof code === 'string' ? { code } : {}),\n ...(typeof retryable === 'boolean' ? { retryable } : {}),\n },\n });\n }\n const message =\n error instanceof Error ? error.message : 'Task operation failed';\n return jsonRpcResponse(body.id, undefined, { code: -32602, message });\n }\n}\n\nfunction taskHeaderMismatch(\n request: Request,\n method: string | undefined,\n params: Record<string, unknown>,\n): { code: number; message: string } | undefined {\n if (\n !method ||\n normalizeHeaderValue(request.headers.get('mcp-method') ?? '') !== method\n ) {\n return {\n code: -32020,\n message: 'Mcp-Method header must match the JSON-RPC method.',\n };\n }\n const toolName =\n method === 'tools/call' && typeof params.name === 'string'\n ? params.name\n : undefined;\n const headerName = request.headers.get('mcp-name');\n if (\n toolName !== undefined &&\n (headerName === null || decodeMcpHeaderValue(headerName) !== toolName)\n ) {\n return {\n code: -32020,\n message: 'Mcp-Name header must match the tools/call name.',\n };\n }\n return undefined;\n}\n\n/** Match the SDK's RFC 9110 optional-whitespace handling for MCP headers. */\nfunction normalizeHeaderValue(value: string): string {\n return value.replace(/^[\\t ]+|[\\t ]+$/g, '');\n}\n\n/** Decode the SDK's canonical Base64 sentinel for an MCP header value. */\nfunction decodeMcpHeaderValue(value: string): string | undefined {\n const normalized = normalizeHeaderValue(value);\n const prefix = '=?base64?';\n if (!normalized.startsWith(prefix) || !normalized.endsWith('?=')) {\n return normalized;\n }\n const encoded = normalized.slice(prefix.length, -2);\n if (\n !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(\n encoded,\n )\n ) {\n return undefined;\n }\n try {\n return new TextDecoder('utf-8', { fatal: true }).decode(\n Uint8Array.from(atob(encoded), (character) => character.charCodeAt(0)),\n );\n } catch {\n return undefined;\n }\n}\n\nfunction jsonRpcResponse(\n id: string | number | null,\n result?: unknown,\n error?: { code: number; message: string; data?: unknown },\n status = 200,\n): Response {\n return new Response(\n JSON.stringify({ jsonrpc: '2.0', id, ...(error ? { error } : { result }) }),\n { status, headers: { 'content-type': 'application/json' } },\n );\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n}\n\n/**\n * Mount `server.listTools` as a `GET` handler. Returns the tool list shape\n * `{ tools }` for compatibility with the stock MCP bridge.\n *\n * @deprecated Use {@link mountMcpRoute}. Existing REST-shaped mounts can use\n * this compatibility alias while migrating without a coordinated cutover.\n */\nexport function mountMcpToolsRoute(\n server: McpAppServer,\n options: MountMcpRouteOptions = {},\n): McpSvelteKitHandler {\n return async (event) => {\n try {\n const tools = await server.listTools(\n listToolsInput(resolveRequestPrincipal(event, options)),\n );\n return jsonResponse({ tools });\n } catch (error) {\n if (error instanceof McpAccessError) {\n return jsonResponse(mcpAccessErrorBody(error), error.status);\n }\n throw error;\n }\n };\n}\n\n/**\n * Mount `server.callTool` as a `POST` handler that expects\n * `{ name, arguments }` in the JSON body.\n *\n * @deprecated Use {@link mountMcpRoute}. Existing REST-shaped mounts can use\n * this compatibility alias while migrating without a coordinated cutover.\n */\nexport function mountMcpCallRoute(\n server: McpAppServer,\n options: MountMcpRouteOptions = {},\n): McpSvelteKitHandler {\n return async (event) => {\n const body = (await event.request.json().catch(() => null)) as {\n arguments?: Record<string, unknown>;\n name?: string;\n } | null;\n\n if (!body?.name) {\n return jsonResponse({ error: 'name is required.' }, 400);\n }\n\n const input: CallToolInput = {\n arguments: body.arguments ?? {},\n name: body.name,\n principal: resolveRequestPrincipal(event, options).principal,\n };\n\n try {\n return jsonResponse(await server.callTool(input));\n } catch (error) {\n if (error instanceof McpAccessError) {\n return jsonResponse(mcpAccessErrorBody(error), error.status);\n }\n throw error;\n }\n };\n}\n\nfunction mcpAccessErrorBody(error: McpAccessError): unknown {\n const { code, retryable } = error.metadata;\n // Preserve the legacy shape unless an error deliberately opts into the\n // shared structured failure contract.\n if (!code) return { error: error.message };\n return {\n error: {\n ok: false,\n code,\n message: error.message,\n status: error.status,\n ...(retryable === undefined ? {} : { retryable }),\n },\n };\n}\n\nfunction jsonResponse(body: unknown, status = 200): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'content-type': 'application/json' },\n });\n}\n\nexport type {\n CreateDefaultMcpAppServerOptions,\n McpAppModel,\n} from './defaults.js';\nexport { McpAccessError } from './errors.js';\nexport type { McpAppPrincipal, McpAppServer } from './server.js';\n"],"mappings":";;;AA0FA,IAAM,2BAAiD,UACpD,MAAM,QAAQ,QAAQ;AAEzB,SAAS,wBACP,OACA,SAC0B;CAG1B,MAAM,aADJ,QAAQ,oBAAoB,QAAQ,eAAe,wBAAA,CAClB,KAAK,KAAK;CAG7C,IACE,CAAC,QAAQ,oBACT,QAAQ,wBACR,CAAC,QAAQ,qBAAqB,KAAK,GAEnC,OAAO;EAAE,WAAW;EAAM,qBAAqB;CAAM;CAEvD,OAAO;EACL;EACA,GAAI,QAAQ,oBAAoB,CAAC,QAAQ,uBACrC,CAAC,IACD,EAAE,qBAAqB,KAAK;CAClC;AACF;AAEA,SAAS,eAAe,UAAoC;CAK1D,IAAI,CAAC,SAAS,aAAa,SAAS,qBAClC,OAAO,EAAE,eAAe,KAAK;CAE/B,OAAO,EAAE,WAAW,SAAS,UAAU;AACzC;AAuBA,SAAS,oBACP,QAC6B;CAE7B,QADc,OAAO,WAAW,aAAa,OAAO,IAAI,WACxC;AAClB;AA2CA,IAAM,6BAA6B;CACjC;CACA;CACA;AACF;AACA,IAAM,4BAA4B;CAChC,GAAG;CACH;CACA;AACF;AACA,IAAM,sBAAyC;CAC7C;CACA;CACA;AACF;AAGA,SAAS,cACP,SACA,SACA,OACM;CACN,MAAM,QAAQ;CACd,MAAM,UACJ,CAAC,SAAS,OAAO,UAAU,WACvB,UACA,QAAQ,QAAQ,WAAW,OAAO,MAAM,YAAY,UAAU;CACpE,IAAI,QAAQ,SAAS,GACnB,MAAM,IAAI,UACR,GAAG,MAAK,yBAA0B,QAAQ,KAAK,IAAI,EAAC,EACtD;AAEJ;AAQA,IAAM,qCAAqB,IAAI,QAG7B;AACF,IAAI;AAGJ,SAAS,iBAAsD;CAC7D,eAAe,OAAO,YAAW,CAAE,OAAO,UAAmB;EAC3D,aAAa,KAAA;EACb,MAAM;CACR,CAAC;CACD,OAAO;AACT;AAUA,eAAe,oBACb,SAC+B;CAE/B,MAAM,WAAW,MADM,QAAQ,gBAAgB,EAAA,EAE3C;CACJ,IAAI,OAAO,YAAY,YAAY,CAAC,oBAAoB,SAAS,OAAO,GACtE,MAAM,IAAI,MAAM,qDAAqD;CAEvE,IAAI,UAAU,mBAAmB,IAAI,OAAO;CAC5C,IAAI,CAAC,SAAS;EACZ,0BAAU,IAAI,IAAI;EAClB,mBAAmB,IAAI,SAAS,OAAO;CACzC;CACA,IAAI,SAAS,QAAQ,IAAI,OAA+B;CACxD,IAAI,CAAC,QAAQ;EACX,MAAM,EAAE,gCAAgC,MAAM,eAAe;EAC7D,SACE,QAAQ,IAAI,OAA+B,KAC3C,4BAA4B;GAC1B;GACA;EACF,CAAC;EACH,QAAQ,IAAI,SAAiC,MAAM;CACrD;CACA,MAAM,UAAU,OAAO;CACvB,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,yDAAyD;CAE3E,OAAO;AACT;AAGA,SAAS,gBAAgB,OAAmC;CAC1D,IAAI,UAAU,QAAQ,OAAO,KAAA;CAC7B,IAAI;CACJ,IAAI;EACF,MAAM,IAAI,IAAI,KAAK;CACrB,QAAQ;EACN;CACF;CACA,IAAI,IAAI,aAAa,YAAY,IAAI,aAAa,SAAS,OAAO,KAAA;CAClE,IAAI,IAAI,YAAY,IAAI,YAAY,IAAI,UAAU,IAAI,MAAM,OAAO,KAAA;CACnE,IAAI,IAAI,aAAa,KAAK,OAAO,KAAA;CACjC,OAAO,IAAI;AACb;AAEA,SAAS,wBACP,QACqB;CACrB,IAAI,CAAC,MAAM,QAAQ,MAAM,GACvB,MAAM,IAAI,UAAU,iDAAiD;CAEvE,MAAM,0BAAU,IAAI,IAAY;CAChC,KAAA,MAAW,SAAS,QAAQ;EAC1B,MAAM,SACJ,OAAO,UAAU,WAAW,gBAAgB,KAAK,IAAI,KAAA;EACvD,IAAI,CAAC,QAAQ,MAAM,IAAI,UAAU,6BAA6B;EAC9D,QAAQ,IAAI,MAAM;CACpB;CACA,OAAO;AACT;AAQA,SAAS,gBACP,OACA,SACS;CACT,MAAM,SAAS,MAAM,QAAQ,QAAQ,IAAI,QAAQ;CACjD,IAAI,WAAW,MAAM;EACnB,MAAM,SAAS,gBAAgB,OAAO,KAAK,CAAC;EAC5C,OACE,WAAW,KAAA,MACV,WAAW,MAAM,IAAI,UAAU,QAAQ,IAAI,MAAM;CAEtD;CAEA,MAAM,OAAO,MAAM,QAAQ,QACxB,IAAI,gBAAgB,CAAA,EACnB,KAAK,CAAA,CACN,YAAY;CACf,OAAO,SAAS,gBAAgB,SAAS;AAC3C;AAEA,SAAS,uBAAiC;CACxC,OAAO,IAAI,SACT,KAAK,UAAU;EACb,SAAS;EACT,IAAI;EACJ,OAAO;GACL,MAAM;GACN,SAAS;GACT,MAAM;IAAE,MAAM;IAAwB,WAAW;GAAM;EACzD;CACF,CAAC,GACD;EACE,QAAQ;EACR,SAAS;GACP,gBAAgB;GAChB,iBAAiB;EACnB;CACF,CACF;AACF;AAYO,SAAS,2BAA2B,OAEhB;CACzB,MAAM,SAAS,MAAM;CACrB,IAAI,CAAC,UAAU,OAAO,WAAW,UAAU,OAAO;CAClD,MAAM,OAAO,OAAO;CACpB,MAAM,WAAW,OAAO;CACxB,MAAM,cAAc,OAAO;CAC3B,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO;CAC9C,IAAI,OAAO,KAAK,OAAO,YAAY,KAAK,GAAG,WAAW,GAAG,OAAO;CAChE,IAAI,OAAO,aAAa,YAAY,SAAS,WAAW,GAAG,OAAO;CAClE,IACE,CAAC,MAAM,QAAQ,WAAW,KAC1B,CAAC,YAAY,OAAO,UAAU,OAAO,UAAU,QAAQ,GAEvD,OAAO;CACT,OAAO;EACL,IAAI,KAAK;EACT;EACA,MAAM;EACN,QAAQ,CAAC,GAAG,WAAW,CAAA,CAAE,KAAK;CAChC;AACF;AAsCA,SAAS,wBACP,eACA,OACgD;CAChD,IAAI,UAAU,KAAA,GAAW,OAAO;CAChC,IACE,CAAC,SACD,OAAO,UAAU,YACjB,MAAM,OAAO,cAAc,OAC1B,MAAM,YAAY,WAAW,cAAc,YAAY,SACxD,CAAC,MAAM,QAAQ,MAAM,MAAM,KAC3B,CAAC,MAAM,OAAO,OAAO,UAAU,OAAO,UAAU,QAAQ,GAExD;CAEF,MAAM,UAAU,IAAI,IAAI,MAAM,MAAM;CACpC,OAAO;EACL,GAAG;EACH,SAAS,cAAc,UAAU,CAAC,EAAA,CAAG,QAAQ,UAAU,QAAQ,IAAI,KAAK,CAAC;CAC3E;AACF;AAyEA,SAAS,yBACP,QACA,UACc;CAId,IAAI,CAAC,SAAS,uBAAuB,SAAS,WAAW,OAAO;CAChE,OAAO;EACL,GAAG;EACH,YAAY,OAAO;EACnB,iBAAiB,OAAO,UAAU,EAAE,eAAe,KAAK,CAAC;EACzD,WAAW,UAAU,OAAO,SAAS,KAAK;CAC5C;AACF;AASO,SAAS,cACd,QACA,UAAgC,CAAC,GACZ;CACrB,OAAO,kBAAkB,QAAQ,eAC/B,oBAAoB,QAAQ,IAAI,CAClC;AACF;AAQA,SAAS,kBACP,QACA,SACA,aAIqB;CACrB,MAAM,iBAAiB,QAAQ,cAC3B,wBAAwB,QAAQ,kBAAkB,CAAC,CAAC,IACpD,KAAA;CACJ,OAAO,OAAO,UAAU;EAGtB,IAAI,kBAAkB,CAAC,gBAAgB,OAAO,cAAc,GAC1D,OAAO,qBAAqB;EAE9B,MAAM,OAAO,MAAM,YAAY;EAC/B,IAAI,CAAC,MAAM,OAAO,SAAS,OAAO,wBAAwB,OAAO,OAAO,CAAC;EACzE,IAAI,CAAC,MAAM,QAAQ,QAAQ,IAAI,eAAe,GAAG;GAK/C,IAAI,KAAK,oBAAoB,MAC3B,OAAO,SAAS,OAAO,wBAAwB,OAAO,OAAO,CAAC;GAEhE,IAAI,QAAQ,mBAAmB,MAC7B,OAAO,SAAS,OAAO,EAAE,WAAW,KAAK,CAAC;EAE9C;EACA,MAAM,UAAU,MAAM,KAAK,aAAa,MAAM,OAAO;EACrD,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ;EAChC,IAAI,CAAC,QAAQ,eACX,OAAO,SAAS,OAAO,EAAE,WAAW,QAAQ,UAAU,CAAC;EAEzD,IAAI,UAAU;EACd,IAAI;EACJ,IAAI;GACF,WAAW,MAAM,QAAQ,cACvB,QAAQ,WACR,OAAO,UAAU;IACf,UAAU;IACV,MAAM,YAAY,wBAAwB,QAAQ,WAAW,KAAK;IAClE,IAAI,CAAC,WAAW,OAAO,yBAAyB;IAGhD,MAAM,aAAa,MAAM,SAAS,OAAO,EAAE,UAAU,CAAC;IACtD,OAAO,IAAI,SAAS,MAAM,WAAW,YAAY,GAAG;KAClD,QAAQ,WAAW;KACnB,YAAY,WAAW;KACvB,SAAS,WAAW;IACtB,CAAC;GACH,CACF;EACF,SAAS,OAAO;GACd,IAAI,SAAS,MAAM;GACnB,OAAO,yBAAyB;EAClC;EACA,OAAO,UAAU,WAAW,yBAAyB;CACvD;CAEA,eAAe,SACb,OACA,UACmB;EACnB,MAAM,eAAe,MAAM,uBACzB,QACA,SAAS,WACT,MAAM,OACR;EACA,IAAI,cAAc,OAAO;EAoBzB,OAnBgB,uBAEZ,kCACE,yBAAyB,QAAQ,QAAQ,GACzC;GACE,WAAW,SAAS;GACpB,YAAY,QAAQ;EACtB,CACF,GACF;GAGE,QAAQ;GAIR,kBAAkB;EACpB,CAEK,CAAA,CAAQ,MAAM,MAAM,OAAO;CACpC;AACF;AAEA,SAAS,2BAAqC;CAC5C,OAAO,IAAI,SACT,KAAK,UAAU;EACb,SAAS;EACT,IAAI;EACJ,OAAO;GACL,MAAM;GACN,SAAS;GACT,MAAM;IAAE,MAAM;IAA6B,WAAW;GAAM;EAC9D;CACF,CAAC,GACD;EACE,QAAQ;EACR,SAAS;GACP,gBAAgB;GAChB,iBAAiB;EACnB;CACF,CACF;AACF;AAkFO,SAAS,iBACd,SACwB;CACxB,MAAM,EACJ,MACA,gBACA,eACA,YACA,kBACA,aACA,gBACA,SACA,GAAG,kBACD;CACJ,IAAI,YAAY,KAAA,GACd,cAAc,SAAS,2BAA2B,kBAAkB;CAEtE,MAAM,SAAS,0BAA0B;EACvC,GAAG;EACH,aACE,cAAc,gBAAgB,KAAA,KAAa,CAAC,UACvC,cAAc,qBACR,EAAE,IAAI,QAAQ,eAAe,EAAE;CAC9C,CAAC;CAcD,MAAM,UAAU,kBACd,QACA;EAdA;EACA;EACA,eACE,kBAAkB,KAAA,KAAa,CAAC,UAC5B,iBACC,WAAW,QAAQ,QAAQ,eAAe,WAAW,GAAG;EAC/D;EAEA,aAAa,eAAe;EAC5B;EACA,kBAAkB,oBAAoB;CAItC,GACA,SAAS,KAAA,KAAa,CAAC,gBACb,oBAAoB,IAAI,UACxB,oBAAoB,OAAO,CACvC;CACA,OAAO,OAAO,eAAe,SAAS,UAAU;EAC9C,OAAO;EACP,YAAY;CACd,CAAC;AACH;AAmCO,SAAS,uCACd,QAGqB;CACrB,IAAI;CAIJ,IACE,WAAW,QACX,OAAO,WAAW,YAClB,OAAO,OAAO,QAAQ,SAAS,GAC/B;EACA,IAAI,OAAO,OAAO,QAAQ,cAAc,GACtC,MAAM,IAAI,UACR,wFACF;EAEF,MAAM,EAAE,SAAS,SACf;EACF,cACE,SACA,4BACA,wCACF;EACA,cACE,SAAS,KAAA,UACC,oBAAoB,IAAI,UACxB,oBAAoB,OAAO;CACzC,OAAO;EACL,MAAM,OAAO;EACb,oBAAoB,oBAAoB,IAAI;CAC9C;CACA,OAAO,OAAO,UAAU;EACtB,MAAM,UAAU,MAAM,YAAY;EAClC,IACE,CAAC,WACD,QAAQ,oBAAoB,QAC5B,MAAM,IAAI,aAAa,IAAI,IAAI,QAAQ,WAAW,CAAA,CAAE,UAEpD,OAAO,IAAI,SAAS,MAAM;GACxB,QAAQ;GACR,SAAS,EAAE,iBAAiB,WAAW;EACzC,CAAC;EACH,OAAO,QAAQ,iBAAiB;CAClC;AACF;AAOA,eAAe,uBACb,QACA,WACA,SAC0B;CAC1B,IACE,CAAC,OAAO,gBACR,CAAC,OAAO,YACR,CAAC,OAAO,WACR,CAAC,OAAO,cACR,CAAC,OAAO,cACR,QAAQ,WAAW,QAEnB,OAAO;CAET,MAAM,OAAQ,MAAM,QACjB,MAAM,CAAA,CACN,KAAK,CAAA,CACL,YAAY,IAAI;CAMnB,IAAI,MAAM,YAAY,SAAS,KAAK,OAAO,KAAA,GAAW,OAAO;CAC7D,MAAM,SAAS,KAAK,UAAU,CAAC;CAC/B,MAAM,WACJ,KAAK,WAAW,gBAChB,OAAO,OAAO,SAAS,YACtB,MAAM,OAAO,aAAa,OAAO,IAAI,MAAO;CAC/C,MAAM,aAAa;EAAC;EAAa;EAAgB;CAAc,CAAA,CAAE,SAC/D,KAAK,UAAU,EACjB;CACA,IAAI,CAAC,YAAY,CAAC,YAAY,OAAO;CAMrC,IAAI,CAAC,kBAAkB,QAAQ,QAAQ,IAAI,cAAc,CAAC,GAAG,OAAO;CACpE,MAAM,iBAAiB,uBAAuB;EAC5C,YAAY,QAAQ;EACpB,uBACE,QAAQ,QAAQ,IAAI,sBAAsB,KAAK,KAAA;EACjD,iBAAiB,QAAQ,QAAQ,IAAI,YAAY,KAAK,KAAA;EACtD,eAAe,QAAQ,QAAQ,IAAI,UAAU,KAAK,KAAA;EAClD;CACF,CAAC;CACD,IAAI,eAAe,SAAS,UAC1B,OAAO,gBACL,KAAK,IACL,KAAA,GACA;EACE,MAAM,eAAe;EACrB,SAAS,eAAe;EACxB,GAAI,eAAe,SAAS,KAAA,IACxB,CAAC,IACD,EAAE,MAAM,eAAe,KAAK;CAClC,GACA,eAAe,UACjB;CAEF,IACE,eAAe,SAAS,YACxB,eAAe,eAAe,aAAa,cAE3C,OAAO;CAMT,MAAM,iBAAiB,mBAAmB,SAAS,KAAK,QAAQ,MAAM;CACtE,IAAI,gBACF,OAAO,gBAAgB,KAAK,IAAI,KAAA,GAAW,gBAAgB,GAAG;CAGhE,MAAM,qBAAqB,SAAS,OAAO,KAAK,CAAA,CAC9C;CAEF,MAAM,sBAAsB,OAAO,OACjC,SAAS,SAAS,kBAAkB,CAAA,CAAE,UAAU,GAChD,mBACF;CAGA,IAAI,CAAC,uBAAuB,KAAK,WAAW,cAAc,OAAO;CACjE,IAAI,CAAC,qBACH,OAAO,gBACL,KAAK,IACL,KAAA,GACA;EACE,MAAM;EACN,SAAS;EACT,MAAM,EACJ,sBAAsB,EAAE,YAAY,GAAG,sBAAsB,CAAC,EAAE,EAAE,EACpE;CACF,GACA,GACF;CAGF,IAAI;EACF,IAAI,KAAK,WAAW,cAAc;GAChC,MAAM,SAAS,MAAM,OAAO,SAAS;IACnC,MAAM,OAAO;IACb,WAAY,OAAO,aAAyC,CAAC;IAC7D;GACF,CAAC;GACD,OAAO,gBAAgB,KAAK,IAAI,MAAM;EACxC;EACA,IAAI,OAAO,OAAO,WAAW,UAC3B,OAAO,gBAAgB,KAAK,IAAI,KAAA,GAAW;GACzC,MAAM;GACN,SAAS;EACX,CAAC;EAEH,IAAI,KAAK,WAAW,aAAa;GAC/B,MAAM,OAAO,MAAM,OAAO,QAAQ;IAAE,QAAQ,OAAO;IAAQ;GAAU,CAAC;GACtE,OAAO,gBAAgB,KAAK,IAAI;IAAE,YAAY;IAAY,GAAG;GAAK,CAAC;EACrE;EACA,IAAI,KAAK,WAAW,gBAAgB;GAClC,MAAM,OAAO,WAAW;IACtB,QAAQ,OAAO;IACf,gBACE,OAAO,kBAAkB,OAAO,OAAO,mBAAmB,WACrD,OAAO,iBACR,CAAC;IACP;GACF,CAAC;GACD,OAAO,gBAAgB,KAAK,IAAI,EAAE,YAAY,WAAW,CAAC;EAC5D;EACA,MAAM,OAAO,WAAW;GAAE,QAAQ,OAAO;GAAQ;EAAU,CAAC;EAC5D,OAAO,gBAAgB,KAAK,IAAI,EAAE,YAAY,WAAW,CAAC;CAC5D,SAAS,OAAO;EACd,IAAI,iBAAiB,gBAAgB;GACnC,MAAM,EAAE,MAAM,cAAc,MAAM;GAClC,OAAO,gBAAgB,KAAK,IAAI,KAAA,GAAW;IACzC,MAAM,MAAM,WAAW,MAAM,SAAS;IACtC,SAAS,MAAM;IACf,MAAM;KACJ,GAAI,OAAO,SAAS,WAAW,EAAE,KAAK,IAAI,CAAC;KAC3C,GAAI,OAAO,cAAc,YAAY,EAAE,UAAU,IAAI,CAAC;IACxD;GACF,CAAC;EACH;EACA,MAAM,UACJ,iBAAiB,QAAQ,MAAM,UAAU;EAC3C,OAAO,gBAAgB,KAAK,IAAI,KAAA,GAAW;GAAE,MAAM;GAAQ;EAAQ,CAAC;CACtE;AACF;AAEA,SAAS,mBACP,SACA,QACA,QAC+C;CAC/C,IACE,CAAC,UACD,qBAAqB,QAAQ,QAAQ,IAAI,YAAY,KAAK,EAAE,MAAM,QAElE,OAAO;EACL,MAAM;EACN,SAAS;CACX;CAEF,MAAM,WACJ,WAAW,gBAAgB,OAAO,OAAO,SAAS,WAC9C,OAAO,OACP,KAAA;CACN,MAAM,aAAa,QAAQ,QAAQ,IAAI,UAAU;CACjD,IACE,aAAa,KAAA,MACZ,eAAe,QAAQ,qBAAqB,UAAU,MAAM,WAE7D,OAAO;EACL,MAAM;EACN,SAAS;CACX;AAGJ;AAGA,SAAS,qBAAqB,OAAuB;CACnD,OAAO,MAAM,QAAQ,oBAAoB,EAAE;AAC7C;AAGA,SAAS,qBAAqB,OAAmC;CAC/D,MAAM,aAAa,qBAAqB,KAAK;CAE7C,IAAI,CAAC,WAAW,WAAW,WAAM,KAAK,CAAC,WAAW,SAAS,IAAI,GAC7D,OAAO;CAET,MAAM,UAAU,WAAW,MAAM,GAAe,EAAE;CAClD,IACE,CAAC,mEAAmE,KAClE,OACF,GAEA;CAEF,IAAI;EACF,OAAO,IAAI,YAAY,SAAS,EAAE,OAAO,KAAK,CAAC,CAAA,CAAE,OAC/C,WAAW,KAAK,KAAK,OAAO,IAAI,cAAc,UAAU,WAAW,CAAC,CAAC,CACvE;CACF,QAAQ;EACN;CACF;AACF;AAEA,SAAS,gBACP,IACA,QACA,OACA,SAAS,KACC;CACV,OAAO,IAAI,SACT,KAAK,UAAU;EAAE,SAAS;EAAO;EAAI,GAAI,QAAQ,EAAE,MAAM,IAAI,EAAE,OAAO;CAAG,CAAC,GAC1E;EAAE;EAAQ,SAAS,EAAE,gBAAgB,mBAAmB;CAAE,CAC5D;AACF;AAEA,SAAS,SAAS,OAAyC;CACzD,OAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IACrE,QACD,CAAC;AACP;AASO,SAAS,mBACd,QACA,UAAgC,CAAC,GACZ;CACrB,OAAO,OAAO,UAAU;EACtB,IAAI;GAIF,OAAO,aAAa,EAAE,OAAA,MAHF,OAAO,UACzB,eAAe,wBAAwB,OAAO,OAAO,CAAC,CACxD,EAC4B,CAAC;EAC/B,SAAS,OAAO;GACd,IAAI,iBAAiB,gBACnB,OAAO,aAAa,mBAAmB,KAAK,GAAG,MAAM,MAAM;GAE7D,MAAM;EACR;CACF;AACF;AASO,SAAS,kBACd,QACA,UAAgC,CAAC,GACZ;CACrB,OAAO,OAAO,UAAU;EACtB,MAAM,OAAQ,MAAM,MAAM,QAAQ,KAAK,CAAA,CAAE,YAAY,IAAI;EAKzD,IAAI,CAAC,MAAM,MACT,OAAO,aAAa,EAAE,OAAO,oBAAoB,GAAG,GAAG;EAGzD,MAAM,QAAuB;GAC3B,WAAW,KAAK,aAAa,CAAC;GAC9B,MAAM,KAAK;GACX,WAAW,wBAAwB,OAAO,OAAO,CAAA,CAAE;EACrD;EAEA,IAAI;GACF,OAAO,aAAa,MAAM,OAAO,SAAS,KAAK,CAAC;EAClD,SAAS,OAAO;GACd,IAAI,iBAAiB,gBACnB,OAAO,aAAa,mBAAmB,KAAK,GAAG,MAAM,MAAM;GAE7D,MAAM;EACR;CACF;AACF;AAEA,SAAS,mBAAmB,OAAgC;CAC1D,MAAM,EAAE,MAAM,cAAc,MAAM;CAGlC,IAAI,CAAC,MAAM,OAAO,EAAE,OAAO,MAAM,QAAQ;CACzC,OAAO,EACL,OAAO;EACL,IAAI;EACJ;EACA,SAAS,MAAM;EACf,QAAQ,MAAM;EACd,GAAI,cAAc,KAAA,IAAY,CAAC,IAAI,EAAE,UAAU;CACjD,EACF;AACF;AAEA,SAAS,aAAa,MAAe,SAAS,KAAe;CAC3D,OAAO,IAAI,SAAS,KAAK,UAAU,IAAI,GAAG;EACxC;EACA,SAAS,EAAE,gBAAgB,mBAAmB;CAChD,CAAC;AACH"}
|
|
1
|
+
{"version":3,"file":"sveltekit.js","names":[],"sources":["../src/sveltekit.ts"],"sourcesContent":["/**\n * SvelteKit route adapters for an `McpAppServer`. Mirrors the\n * `@happyvertical/smrt-users/sveltekit` pattern: minimal `HandleInput` type\n * so we never need `@sveltejs/kit` as a real dependency.\n *\n * @packageDocumentation\n *\n * @example\n * ```ts\n * // src/routes/api/mcp/+server.ts — one call with the app defaults\n * import { mountMcpAppRoute } from '@happyvertical/smrt-app-mcp/sveltekit';\n * import { Item } from '$lib/objects/Item';\n * import { runtime } from '$lib/server/smrt';\n * export const POST = mountMcpAppRoute({\n * runtime,\n * models: [Item],\n * requiredScopes: ['items.read'],\n * effects: ['read'],\n * });\n * ```\n *\n * @example\n * ```ts\n * // src/routes/api/mcp/+server.ts — a custom server core\n * import { mountMcpRoute } from '@happyvertical/smrt-app-mcp/sveltekit';\n * import { mcpServer } from '$lib/server/mcp';\n * export const POST = mountMcpRoute(mcpServer);\n * ```\n */\n\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport {\n classifyInboundRequest,\n createMcpHandler,\n isJsonContentType,\n} from '@modelcontextprotocol/server';\nimport type {\n McpDeploymentProfile,\n McpLocalTokenPrincipal,\n McpPrincipalMapping,\n McpResourceAuth,\n McpVerifiedIdentity,\n} from './auth.js';\nimport {\n type CreateDefaultMcpAppServerOptions,\n createDefaultMcpAppServer,\n} from './defaults.js';\nimport {\n MCP_ORIGIN_DENIED_CODE,\n MCP_TOOL_ACCESS_DENIED_CODE,\n McpAccessError,\n} from './errors.js';\nimport { stampVerifiedIdentity } from './identity.js';\nimport {\n createMcpProtocolServerForRequest,\n MCP_TASKS_EXTENSION,\n type McpProtocolRequestOptions,\n} from './protocol.js';\nimport type {\n CallToolInput,\n McpAppPrincipal,\n McpAppServer,\n McpSmrtOptionsThunk,\n} from './server.js';\n\n/** Minimal subset of a SvelteKit RequestEvent we actually touch. */\ntype SvelteKitRequestEvent = {\n locals?: Record<string, unknown>;\n request: Request;\n url: URL;\n};\n\n/** A SvelteKit `+server.ts` request handler with no SvelteKit dependency. */\nexport type McpSvelteKitHandler = (\n event: SvelteKitRequestEvent,\n) => Promise<Response>;\n\ntype ResolvedRequestPrincipal = {\n principal: McpAppPrincipal | null;\n /** Defined only for the legacy discovery-only authentication adapter. */\n legacyAuthenticated?: boolean;\n};\n\n/** Locals reader used to pull the request principal out of `event.locals`. */\nexport type McpPrincipalResolver = (\n event: SvelteKitRequestEvent,\n) => McpAppPrincipal | null | undefined;\n\n/** Backwards-compatible alias for callers that name the principal a user. */\nexport type McpUserResolver = McpPrincipalResolver;\n\nconst defaultResolvePrincipal: McpPrincipalResolver = (event) =>\n (event.locals?.user ?? null) as McpAppPrincipal | null;\n\nfunction resolveRequestPrincipal(\n event: SvelteKitRequestEvent,\n options: MountMcpRouteOptions,\n): ResolvedRequestPrincipal {\n const resolvePrincipal =\n options.resolvePrincipal ?? options.resolveUser ?? defaultResolvePrincipal;\n const principal = resolvePrincipal(event) ?? null;\n // Keep the legacy boolean gate for existing apps, but apply it to the same\n // principal that both routes receive. A new principal resolver supersedes it.\n if (\n !options.resolvePrincipal &&\n options.resolveAuthenticated &&\n !options.resolveAuthenticated(event)\n ) {\n return { principal: null, legacyAuthenticated: false };\n }\n return {\n principal,\n ...(options.resolvePrincipal || !options.resolveAuthenticated\n ? {}\n : { legacyAuthenticated: true }),\n };\n}\n\nfunction listToolsInput(resolved: ResolvedRequestPrincipal) {\n // Before principal-aware routes, `resolveAuthenticated` affected discovery\n // independently of `resolveUser`. Preserve a true legacy result only when\n // there is no principal to pass; new routes should use `resolvePrincipal`\n // for a single identity on both discovery and calls.\n if (!resolved.principal && resolved.legacyAuthenticated) {\n return { authenticated: true };\n }\n return { principal: resolved.principal };\n}\n\n/**\n * The bearer-authentication surface `mountMcpRoute` and the metadata route\n * need from `createMcpResourceAuth` (`./auth`). Typed structurally so this\n * entry does not load the JWT verifier.\n */\nexport type McpRouteResourceAuth = Pick<\n McpResourceAuth,\n 'metadataUrl' | 'metadataResponse' | 'authenticate' | 'sessionFallback'\n>;\n\n/**\n * A protected-resource adapter, or a thunk read on every request. A `null` or\n * `undefined` result (for example the `local` profile) means the route uses\n * its session principal resolver instead.\n */\nexport type McpRouteResourceAuthSource =\n | McpRouteResourceAuth\n | null\n | undefined\n | (() => McpRouteResourceAuth | null | undefined);\n\nfunction currentResourceAuth(\n source: McpRouteResourceAuthSource,\n): McpRouteResourceAuth | null {\n const value = typeof source === 'function' ? source() : source;\n return value ?? null;\n}\n\n/**\n * The runtime calls the protected-resource metadata route (and the bearer\n * half of {@link mountMcpAppRoute}) use: the deployment profile and the two\n * credential bindings `createHostedMcpResourceAuth` (`./auth`) takes. The\n * runtime from `@happyvertical/smrt-app-runtime/sveltekit` satisfies it as is.\n */\nexport interface McpRouteAuthRuntime {\n /** Resolve the deployment profile (the SMRT runtime memoizes it). */\n resolvedRuntime(): Promise<{ readonly profile: McpDeploymentProfile }>;\n /**\n * Verify an owner-minted local MCP token (`smrt app token`); `null` denies.\n * Consulted only in the `local` profile.\n */\n verifyLocalMcpToken(token: string): Promise<McpLocalTokenPrincipal | null>;\n /**\n * Map a verified hosted access-token identity to the current account and\n * tenant; `null` denies. Consulted only in hosted profiles.\n */\n resolveMcpPrincipal(\n identity: McpVerifiedIdentity,\n ): Promise<McpPrincipalMapping | null>;\n}\n\n/**\n * The runtime calls {@link mountMcpAppRoute} uses; the runtime from\n * `@happyvertical/smrt-app-runtime/sveltekit` satisfies it as is.\n */\nexport interface McpAppRouteRuntime extends McpRouteAuthRuntime {\n /** The request's database (its RLS transaction inside one). */\n databaseConfig(): SmrtClassOptions['db'];\n /**\n * Run `run` bound to a bearer-authenticated principal (see\n * {@link McpPrincipalBinder}): under `database-rls` in a fresh transaction\n * publishing that user, tenant and live permissions capped to its scopes.\n */\n runAsPrincipal<T>(\n principal: McpAppPrincipal & { id: string },\n run: (bound?: McpBoundPrincipal) => Promise<T>,\n ): Promise<T>;\n}\n\nconst ROUTE_AUTH_RUNTIME_METHODS = [\n 'resolvedRuntime',\n 'verifyLocalMcpToken',\n 'resolveMcpPrincipal',\n] as const;\nconst APP_ROUTE_RUNTIME_METHODS = [\n ...ROUTE_AUTH_RUNTIME_METHODS,\n 'databaseConfig',\n 'runAsPrincipal',\n] as const;\nconst DEPLOYMENT_PROFILES: readonly string[] = [\n 'local',\n 'self-hosted',\n 'cloud',\n];\n\n/** Fail closed at mount: a runtime missing any binding is a wiring error. */\nfunction assertRuntime(\n runtime: unknown,\n methods: readonly string[],\n mount: string,\n): void {\n const value = runtime as Record<string, unknown> | null;\n const missing =\n !value || typeof value !== 'object'\n ? methods\n : methods.filter((method) => typeof value[method] !== 'function');\n if (missing.length > 0) {\n throw new TypeError(\n `${mount}: runtime must provide ${missing.join(', ')}.`,\n );\n }\n}\n\n/**\n * One `createHostedMcpResourceAuth` source per runtime and profile, shared by\n * the app route and the metadata route like the module an app used to keep.\n * Keyed by profile, so a runtime whose profile changes never reuses another\n * profile's adapter; each source keeps its own caching and retry rules.\n */\nconst runtimeAuthSources = new WeakMap<\n object,\n Map<McpDeploymentProfile, () => McpRouteResourceAuth | null>\n>();\nlet authModule: Promise<typeof import('./auth.js')> | undefined;\n\n/** Load `./auth` (the JWT verifier) only when a runtime-derived route needs it. */\nfunction loadAuthModule(): Promise<typeof import('./auth.js')> {\n authModule ??= import('./auth.js').catch((error: unknown) => {\n authModule = undefined;\n throw error;\n });\n return authModule;\n}\n\n/**\n * The bearer adapter a runtime provides for its profile, resolved once per\n * request. Rejects (the route answers 500, before any dispatch) when the\n * profile cannot be resolved or is unknown, when hosted configuration is\n * missing, or when no adapter results: a runtime route never serves a\n * request without its profile's bearer adapter (which would ignore a\n * presented bearer and leave only the session principal).\n */\nasync function runtimeResourceAuth(\n runtime: McpRouteAuthRuntime,\n): Promise<McpRouteResourceAuth> {\n const resolved = await runtime.resolvedRuntime();\n const profile = (resolved as { profile?: unknown } | null | undefined)\n ?.profile;\n if (typeof profile !== 'string' || !DEPLOYMENT_PROFILES.includes(profile)) {\n throw new Error('MCP runtime resolved an unknown deployment profile.');\n }\n let sources = runtimeAuthSources.get(runtime);\n if (!sources) {\n sources = new Map();\n runtimeAuthSources.set(runtime, sources);\n }\n let source = sources.get(profile as McpDeploymentProfile);\n if (!source) {\n const { createHostedMcpResourceAuth } = await loadAuthModule();\n source =\n sources.get(profile as McpDeploymentProfile) ??\n createHostedMcpResourceAuth({\n profile: profile as McpDeploymentProfile,\n runtime,\n });\n sources.set(profile as McpDeploymentProfile, source);\n }\n const adapter = source();\n if (!adapter) {\n throw new Error('MCP runtime provided no bearer adapter for its profile.');\n }\n return adapter;\n}\n\n/** Parse an origin to its canonical `scheme://host[:port]`, or `undefined`. */\nfunction canonicalOrigin(value: string): string | undefined {\n if (value === 'null') return undefined;\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return undefined;\n }\n if (url.protocol !== 'https:' && url.protocol !== 'http:') return undefined;\n if (url.username || url.password || url.search || url.hash) return undefined;\n if (url.pathname !== '/') return undefined;\n return url.origin;\n}\n\nfunction normalizeTrustedOrigins(\n values: readonly string[],\n): ReadonlySet<string> {\n if (!Array.isArray(values)) {\n throw new TypeError('MCP trustedOrigins must be an array of origins.');\n }\n const origins = new Set<string>();\n for (const value of values) {\n const origin =\n typeof value === 'string' ? canonicalOrigin(value) : undefined;\n if (!origin) throw new TypeError('Invalid MCP trusted origin.');\n origins.add(origin);\n }\n return origins;\n}\n\n/**\n * Same-origin is judged against `event.url`, which SvelteKit derives from the\n * adapter's configured origin (for example adapter-node `ORIGIN`, or its\n * `PROTOCOL_HEADER`/`HOST_HEADER` proxy settings). This package does not read\n * `X-Forwarded-*` itself.\n */\nfunction originPermitted(\n event: SvelteKitRequestEvent,\n trusted: ReadonlySet<string>,\n): boolean {\n const header = event.request.headers.get('origin');\n if (header !== null) {\n const origin = canonicalOrigin(header.trim());\n return (\n origin !== undefined &&\n (origin === event.url.origin || trusted.has(origin))\n );\n }\n // No Origin: a non-browser client, unless fetch metadata says otherwise.\n const site = event.request.headers\n .get('sec-fetch-site')\n ?.trim()\n .toLowerCase();\n return site !== 'cross-site' && site !== 'same-site';\n}\n\nfunction originDeniedResponse(): Response {\n return new Response(\n JSON.stringify({\n jsonrpc: '2.0',\n id: null,\n error: {\n code: -32600,\n message: 'MCP request origin is not permitted.',\n data: { code: MCP_ORIGIN_DENIED_CODE, retryable: false },\n },\n }),\n {\n status: 403,\n headers: {\n 'content-type': 'application/json',\n 'cache-control': 'no-store',\n },\n },\n );\n}\n\n/**\n * Project the session hook's request-local result onto an MCP principal.\n *\n * Reads only `locals.user.id`, the session-authorized `locals.tenantId`, and\n * `locals.permissions` as populated by `createSessionHandler` from\n * `@happyvertical/smrt-users/sveltekit`. Headers, URL-selected tenants\n * (`locals.selectedTenantId`), request bodies and route arguments are never\n * identity inputs. Any missing or malformed field yields `null`\n * (unauthenticated), which fails closed for every non-public tool.\n */\nexport function principalFromSessionLocals(event: {\n locals?: Record<string, unknown>;\n}): McpAppPrincipal | null {\n const locals = event.locals;\n if (!locals || typeof locals !== 'object') return null;\n const user = locals.user as { id?: unknown } | null | undefined;\n const tenantId = locals.tenantId;\n const permissions = locals.permissions;\n if (!user || typeof user !== 'object') return null;\n if (typeof user.id !== 'string' || user.id.length === 0) return null;\n if (typeof tenantId !== 'string' || tenantId.length === 0) return null;\n if (\n !Array.isArray(permissions) ||\n !permissions.every((value) => typeof value === 'string')\n )\n return null;\n return {\n id: user.id,\n tenantId,\n kind: 'human',\n scopes: [...permissions].sort(),\n };\n}\n\n/**\n * Run MCP dispatch for a bearer-authenticated principal inside an\n * application-owned context bound to that principal (for example\n * `runtime.runAsPrincipal` from `@happyvertical/smrt-app-runtime/sveltekit`,\n * which opens the principal's permission context and, under `database-rls`,\n * its RLS transaction). Must call `run` exactly once and return its result;\n * throwing before `run` (an unbindable principal) is a denial.\n *\n * Pass `run` the bound principal carrying its *effective* `scopes` (for\n * example the token scopes still granted by live membership permissions).\n * Dispatch then authorizes with those scopes, intersected with the token's:\n * a binder can only narrow authority, and changing the principal's `id` or\n * `tenantId` is a denial. Calling `run()` with no argument keeps the token\n * scopes.\n */\nexport type McpPrincipalBinder = <T>(\n principal: McpAppPrincipal & { id: string },\n run: (bound?: McpBoundPrincipal) => Promise<T>,\n) => Promise<T>;\n\n/**\n * The principal a binder hands back to `run`: the authenticated identity,\n * possibly as a frozen snapshot whose arrays are readonly (for example the\n * deep-frozen principal `runtime.runAsPrincipal` passes its callback).\n */\nexport type McpBoundPrincipal = Omit<McpAppPrincipal, 'scopes' | 'roles'> & {\n id: string;\n scopes?: readonly string[];\n roles?: readonly string[];\n};\n\n/**\n * The principal dispatch authorizes after binding: the authenticated identity\n * with only the scopes both the token and the binder grant. `undefined` when\n * the binder changed the identity or returned malformed scopes.\n */\nfunction effectiveBoundPrincipal(\n authenticated: McpAppPrincipal & { id: string },\n bound: McpBoundPrincipal | undefined,\n): (McpAppPrincipal & { id: string }) | undefined {\n if (bound === undefined) return authenticated;\n if (\n !bound ||\n typeof bound !== 'object' ||\n bound.id !== authenticated.id ||\n (bound.tenantId ?? null) !== (authenticated.tenantId ?? null) ||\n !Array.isArray(bound.scopes) ||\n !bound.scopes.every((scope) => typeof scope === 'string')\n ) {\n return undefined;\n }\n const granted = new Set(bound.scopes);\n return stampVerifiedIdentity(\n {\n ...authenticated,\n scopes: (authenticated.scopes ?? []).filter((scope) =>\n granted.has(scope),\n ),\n },\n authenticated.identity,\n );\n}\n\n/** Options shared by both route mounts. */\nexport interface MountMcpRouteOptions {\n /**\n * Bind a bearer-authenticated principal around dispatch (see\n * {@link McpPrincipalBinder}). The `smrtOptions` database thunk, task\n * handling and tool execution all run inside the binding, and the response\n * is fully materialized before it returns. A binder that throws before\n * running dispatch, or returns without running it, yields HTTP 403 with the\n * safe `mcp_tool_access_denied` JSON-RPC error. Scopes the binder hands\n * back become the effective authority for the tool policy, so live\n * revocation applies even without a database-enforced policy. Used only when `auth`\n * authenticated the request; the session-locals path is unchanged.\n */\n bindPrincipal?: McpPrincipalBinder;\n /**\n * Refuse a modern-endpoint request whose `Origin` header is present but is\n * neither the request URL's own origin nor in `trustedOrigins` (and, when\n * `Origin` is absent, one whose `Sec-Fetch-Site` is `cross-site` or\n * `same-site`). The check runs before bearer authentication, principal\n * resolution and any task or tool dispatch, and answers 403 with the safe\n * `mcp_origin_denied` JSON-RPC error. Requests without browser origin\n * signals (server-side MCP clients, the `smrt-app-cli` stdio bridge) are\n * unaffected. Off unless set for `mountMcpRoute`; on by default for\n * `mountMcpAppRoute`. Ignored by the deprecated REST-shaped mounts.\n */\n checkOrigin?: boolean;\n /**\n * Additional exact origins (`scheme://host[:port]`) accepted by the origin\n * check, e.g. a browser-hosted MCP client, or the public origin when a\n * proxy makes `event.url` differ from what browsers send. `null`, wildcards,\n * paths and credentials are rejected at mount time.\n */\n trustedOrigins?: readonly string[];\n /**\n * Optional bearer authentication for the modern `mountMcpRoute` endpoint.\n * When the source yields an adapter, every request must carry a valid\n * bearer token: a failed check returns the adapter's challenge response\n * before any dispatch, and the adapter's mapped principal replaces the\n * session principal (`resolvePrincipal` is not consulted). When it yields\n * `null`, the route resolves its principal from the request as usual. A\n * `sessionFallback` adapter (the local owner-token adapter) challenges only\n * requests that carry an `Authorization` header; others keep the session\n * principal. Set `allowAnonymous` to permit a headerless request with a\n * non-fallback adapter. Ignored by the deprecated REST-shaped mounts.\n */\n auth?: McpRouteResourceAuthSource;\n /**\n * Permit an anonymous request when an auth adapter is configured but no\n * Authorization header is present. The server policy still exposes only\n * explicitly public read-only tools and resources. A presented bearer is\n * always verified; a bad token never falls back to anonymous access.\n */\n allowAnonymous?: boolean;\n /** Optional extension discovery projected from the request-authorized tool catalog. */\n extensions?: McpProtocolRequestOptions['extensions'];\n /**\n * Resolve the request principal once for both discovery and direct calls.\n * Defaults to `event.locals.user`.\n */\n resolvePrincipal?: McpPrincipalResolver;\n /**\n * Backwards-compatible alias for `resolvePrincipal`.\n */\n resolveUser?: McpUserResolver;\n /**\n * Deprecated legacy authentication gate. When `resolvePrincipal` is not\n * supplied, a false result makes the principal null for both routes.\n */\n resolveAuthenticated?: (event: SvelteKitRequestEvent) => boolean;\n}\n\nfunction protocolServerForRequest(\n server: McpAppServer,\n resolved: ResolvedRequestPrincipal,\n): McpAppServer {\n // Older applications sometimes used a boolean discovery-only adapter. Keep\n // its positive result intact for the deprecated resolver while new mounts\n // consistently use the principal on both MCP methods.\n if (!resolved.legacyAuthenticated || resolved.principal) return server;\n return {\n ...server,\n serverInfo: server.serverInfo,\n listTools: () => server.listTools({ authenticated: true }),\n callTool: (input) => server.callTool(input),\n };\n}\n\n/**\n * Mount a modern, stateless Streamable HTTP MCP endpoint as a SvelteKit\n * `POST` handler. The scoped SDK validates the 2026-07-28 envelope plus the\n * required `Mcp-Method` and `Mcp-Name` headers, returning `-32020` on a\n * mismatch. A fresh protocol server is created for each HTTP request, so this\n * route holds neither MCP sessions nor request principal state between nodes.\n */\nexport function mountMcpRoute(\n server: McpAppServer,\n options: MountMcpRouteOptions = {},\n): McpSvelteKitHandler {\n return mountMcpRouteWith(server, options, () =>\n currentResourceAuth(options.auth),\n );\n}\n\n/**\n * {@link mountMcpRoute} with the bearer adapter supplied per request by\n * `resolveAuth` (sync for an `auth` source, async for a runtime). It runs\n * after the origin check and before any principal or dispatch work; a throw\n * or rejection rejects the handler (no dispatch).\n */\nfunction mountMcpRouteWith(\n server: McpAppServer,\n options: MountMcpRouteOptions,\n resolveAuth: () =>\n | McpRouteResourceAuth\n | null\n | Promise<McpRouteResourceAuth | null>,\n): McpSvelteKitHandler {\n const trustedOrigins = options.checkOrigin\n ? normalizeTrustedOrigins(options.trustedOrigins ?? [])\n : undefined;\n return async (event) => {\n // Ambient browser credentials (session cookies) must not let another\n // origin drive this endpoint; refuse before any identity or dispatch work.\n if (trustedOrigins && !originPermitted(event, trustedOrigins)) {\n return originDeniedResponse();\n }\n const auth = await resolveAuth();\n if (!auth) return dispatch(event, resolveRequestPrincipal(event, options));\n if (!event.request.headers.has('authorization')) {\n // A session-fallback adapter (local owner tokens) keeps its established\n // session path. Explicit anonymous access is separate: it deliberately\n // ignores session locals and custom resolvers, so bearer auth cannot be\n // bypassed through ambient authority.\n if (auth.sessionFallback === true) {\n return dispatch(event, resolveRequestPrincipal(event, options));\n }\n if (options.allowAnonymous === true) {\n return dispatch(event, { principal: null });\n }\n }\n const checked = await auth.authenticate(event.request);\n if (!checked.ok) return checked.response;\n if (!options.bindPrincipal) {\n return dispatch(event, { principal: checked.principal });\n }\n let entered = false;\n let response: Response;\n try {\n response = await options.bindPrincipal(\n checked.principal,\n async (bound) => {\n entered = true;\n const principal = effectiveBoundPrincipal(checked.principal, bound);\n if (!principal) return principalUnboundResponse();\n // Materialize the body inside the binding so no dispatch work can\n // outlive the principal's context (or its RLS transaction).\n const dispatched = await dispatch(event, { principal });\n return new Response(await dispatched.arrayBuffer(), {\n status: dispatched.status,\n statusText: dispatched.statusText,\n headers: dispatched.headers,\n });\n },\n );\n } catch (error) {\n if (entered) throw error;\n return principalUnboundResponse();\n }\n return entered ? response : principalUnboundResponse();\n };\n\n async function dispatch(\n event: SvelteKitRequestEvent,\n resolved: ResolvedRequestPrincipal,\n ): Promise<Response> {\n const taskResponse = await maybeHandleTaskRequest(\n server,\n resolved.principal,\n event.request,\n );\n if (taskResponse) return taskResponse;\n const handler = createMcpHandler(\n () =>\n createMcpProtocolServerForRequest(\n protocolServerForRequest(server, resolved),\n {\n principal: resolved.principal,\n extensions: options.extensions,\n },\n ),\n {\n // The legacy REST-shaped mounts below remain a deprecated migration\n // path. This endpoint accepts only the modern MCP protocol.\n legacy: 'reject',\n // The SDK validates every request before consulting its listen router.\n // Zero capacity keeps this mount stateless by refusing a\n // listen request before it can open an SSE response.\n maxSubscriptions: 0,\n },\n );\n return handler.fetch(event.request);\n }\n}\n\nfunction principalUnboundResponse(): Response {\n return new Response(\n JSON.stringify({\n jsonrpc: '2.0',\n id: null,\n error: {\n code: -32600,\n message: 'MCP tool access is not permitted.',\n data: { code: MCP_TOOL_ACCESS_DENIED_CODE, retryable: false },\n },\n }),\n {\n status: 403,\n headers: {\n 'content-type': 'application/json',\n 'cache-control': 'no-store',\n },\n },\n );\n}\n\n/** Options for {@link mountMcpAppRoute}. */\nexport interface MountMcpAppRouteOptions\n extends CreateDefaultMcpAppServerOptions,\n Pick<\n MountMcpRouteOptions,\n | 'auth'\n | 'bindPrincipal'\n | 'extensions'\n | 'checkOrigin'\n | 'trustedOrigins'\n | 'allowAnonymous'\n > {\n /**\n * Resolve the request principal when no bearer adapter is active. Defaults\n * to {@link principalFromSessionLocals}.\n */\n resolvePrincipal?: McpPrincipalResolver;\n /**\n * The application runtime (`runtime` from\n * `@happyvertical/smrt-app-runtime/sveltekit`). It supplies the three\n * bindings an app otherwise wires by hand, each still overridable:\n * `smrtOptions` defaults to `() => ({ db: runtime.databaseConfig() })`,\n * `bindPrincipal` to `runtime.runAsPrincipal`, and `auth` to\n * `createHostedMcpResourceAuth({ profile, runtime })` for the profile\n * resolved per request (`runtime.resolvedRuntime()`, after the origin\n * check), so the route module needs no top-level `await`. Fails closed: a\n * runtime missing a binding throws at mount, and a profile that cannot be\n * resolved, or a hosted profile without its configuration, rejects the\n * request (HTTP 500) before dispatch instead of serving it without the\n * profile's bearer adapter. An explicit `auth` (including `null`) is used\n * as given.\n */\n runtime?: McpAppRouteRuntime;\n}\n\n/**\n * {@link MountMcpAppRouteOptions} with a `runtime`, which makes `smrtOptions`\n * optional (it defaults to the runtime's request database).\n */\nexport interface MountMcpAppRuntimeRouteOptions\n extends Omit<MountMcpAppRouteOptions, 'runtime' | 'smrtOptions'> {\n runtime: McpAppRouteRuntime;\n /** Default: `() => ({ db: runtime.databaseConfig() })`. */\n smrtOptions?: McpSmrtOptionsThunk;\n}\n\n/** A mounted app route; `server` is the policy core it serves. */\nexport type McpAppSvelteKitHandler = McpSvelteKitHandler & {\n readonly server: McpAppServer;\n};\n\n/**\n * One-call application MCP endpoint: the app's declared `models`, the default\n * principal scope policy (see `createDefaultMcpAppServer`), principals from\n * the SvelteKit session locals, and optional bearer authentication, mounted\n * as the stateless {@link mountMcpRoute} `POST` endpoint. The origin check\n * (`checkOrigin`) is on by default because the default principal is an\n * ambient session cookie; set `trustedOrigins` for extra browser origins or\n * `checkOrigin: false` to opt out.\n *\n * With `runtime`, the request database, the bearer adapter (local owner\n * tokens or hosted access tokens, per the resolved profile) and the principal\n * binding come from the application runtime (see\n * {@link MountMcpAppRouteOptions.runtime}).\n *\n * @example\n * ```ts\n * // src/routes/api/mcp/+server.ts\n * import { mountMcpAppRoute } from '@happyvertical/smrt-app-mcp/sveltekit';\n * import { Item } from '$lib/objects/Item';\n * import { runtime } from '$lib/server/smrt';\n *\n * export const POST = mountMcpAppRoute({\n * runtime,\n * models: [Item],\n * requiredScopes: ['items.read'],\n * effects: ['read'],\n * });\n * ```\n */\nexport function mountMcpAppRoute(\n options: MountMcpAppRouteOptions | MountMcpAppRuntimeRouteOptions,\n): McpAppSvelteKitHandler {\n const {\n auth,\n allowAnonymous,\n bindPrincipal,\n extensions,\n resolvePrincipal,\n checkOrigin,\n trustedOrigins,\n runtime,\n ...serverOptions\n } = options;\n if (runtime !== undefined) {\n assertRuntime(runtime, APP_ROUTE_RUNTIME_METHODS, 'mountMcpAppRoute');\n }\n const server = createDefaultMcpAppServer({\n ...serverOptions,\n smrtOptions:\n serverOptions.smrtOptions !== undefined || !runtime\n ? (serverOptions.smrtOptions as McpSmrtOptionsThunk)\n : () => ({ db: runtime.databaseConfig() }),\n });\n const routeOptions: MountMcpRouteOptions = {\n auth,\n allowAnonymous,\n bindPrincipal:\n bindPrincipal !== undefined || !runtime\n ? bindPrincipal\n : (principal, run) => runtime.runAsPrincipal(principal, run),\n extensions,\n // Default on: the default principal is an ambient session cookie.\n checkOrigin: checkOrigin ?? true,\n trustedOrigins,\n resolvePrincipal: resolvePrincipal ?? principalFromSessionLocals,\n };\n const handler = mountMcpRouteWith(\n server,\n routeOptions,\n auth !== undefined || !runtime\n ? () => currentResourceAuth(auth)\n : () => runtimeResourceAuth(runtime),\n );\n return Object.defineProperty(handler, 'server', {\n value: server,\n enumerable: true,\n }) as McpAppSvelteKitHandler;\n}\n\n/** Options form of {@link mountMcpProtectedResourceMetadataRoute}. */\nexport interface MountMcpProtectedResourceMetadataRouteOptions {\n /** The application runtime; derives the adapter per request. */\n runtime: McpRouteAuthRuntime;\n /** Explicit adapter source; when set, used as given instead of `runtime`. */\n auth?: McpRouteResourceAuthSource;\n}\n\n/**\n * Mount the RFC 9728 protected-resource metadata document as a SvelteKit\n * `GET` handler, at the exact `metadataUrl` path the bearer challenge\n * advertises (for `/api/mcp`:\n * `src/routes/.well-known/oauth-protected-resource/api/mcp/+server.ts`).\n *\n * Returns 404 when the source yields no adapter or the local owner-token\n * adapter (the `local` profile), or when the request path is not the\n * advertised metadata path. This handler does\n * not implement an OAuth authorization server; the metadata names the\n * operator-owned issuer.\n *\n * Pass `{ runtime }` to derive the adapter the way\n * {@link mountMcpAppRoute} does (the profile resolved per request, the same\n * cached adapter as the app route); an unresolvable profile or missing hosted\n * configuration rejects the request (HTTP 500).\n *\n * @example\n * ```ts\n * // src/routes/.well-known/oauth-protected-resource/api/mcp/+server.ts\n * import { mountMcpProtectedResourceMetadataRoute } from '@happyvertical/smrt-app-mcp/sveltekit';\n * import { runtime } from '$lib/server/smrt';\n * export const GET = mountMcpProtectedResourceMetadataRoute({ runtime });\n * ```\n */\nexport function mountMcpProtectedResourceMetadataRoute(\n source:\n | McpRouteResourceAuthSource\n | MountMcpProtectedResourceMetadataRouteOptions,\n): McpSvelteKitHandler {\n let resolveAuth: () =>\n | McpRouteResourceAuth\n | null\n | Promise<McpRouteResourceAuth | null>;\n if (\n source !== null &&\n typeof source === 'object' &&\n Object.hasOwn(source, 'runtime')\n ) {\n if (Object.hasOwn(source, 'authenticate')) {\n throw new TypeError(\n 'mountMcpProtectedResourceMetadataRoute: pass an auth adapter or { runtime }, not both.',\n );\n }\n const { runtime, auth } =\n source as MountMcpProtectedResourceMetadataRouteOptions;\n assertRuntime(\n runtime,\n ROUTE_AUTH_RUNTIME_METHODS,\n 'mountMcpProtectedResourceMetadataRoute',\n );\n resolveAuth =\n auth !== undefined\n ? () => currentResourceAuth(auth)\n : () => runtimeResourceAuth(runtime);\n } else {\n const auth = source as McpRouteResourceAuthSource;\n resolveAuth = () => currentResourceAuth(auth);\n }\n return async (event) => {\n const current = await resolveAuth();\n if (\n !current ||\n current.sessionFallback === true ||\n event.url.pathname !== new URL(current.metadataUrl).pathname\n )\n return new Response(null, {\n status: 404,\n headers: { 'Cache-Control': 'no-store' },\n });\n return current.metadataResponse();\n };\n}\n\n/**\n * The installed MCP SDK validates tools/call against a pre-Tasks result codec.\n * Intercept the extension before the SDK handler so the regular stateless HTTP\n * protocol stays untouched for every other method.\n */\nasync function maybeHandleTaskRequest(\n server: McpAppServer,\n principal: McpAppPrincipal | null,\n request: Request,\n): Promise<Response | null> {\n if (\n !server.tasksEnabled ||\n !server.callTask ||\n !server.getTask ||\n !server.updateTask ||\n !server.cancelTask ||\n request.method !== 'POST'\n ) {\n return null;\n }\n const body = (await request\n .clone()\n .json()\n .catch(() => null)) as {\n id?: string | number | null;\n jsonrpc?: string;\n method?: string;\n params?: Record<string, unknown>;\n } | null;\n if (body?.jsonrpc !== '2.0' || body.id === undefined) return null;\n const params = body.params ?? {};\n const taskTool =\n body.method === 'tools/call' &&\n typeof params.name === 'string' &&\n (await server.isTaskTool?.(params.name)) === true;\n const taskMethod = ['tasks/get', 'tasks/update', 'tasks/cancel'].includes(\n body.method ?? '',\n );\n if (!taskTool && !taskMethod) return null;\n\n // Keep task requests on the SDK's protocol-validation path until their\n // 2026 request envelope is known-good. In particular, never enqueue durable\n // work for malformed envelopes, unsupported revisions, or non-JSON bodies.\n // The fallback handler owns its wire-exact error response for those cases.\n if (!isJsonContentType(request.headers.get('content-type'))) return null;\n const classification = classifyInboundRequest({\n httpMethod: request.method,\n protocolVersionHeader:\n request.headers.get('mcp-protocol-version') ?? undefined,\n mcpMethodHeader: request.headers.get('mcp-method') ?? undefined,\n mcpNameHeader: request.headers.get('mcp-name') ?? undefined,\n body: body as never,\n });\n if (classification.kind === 'reject') {\n return jsonRpcResponse(\n body.id,\n undefined,\n {\n code: classification.code,\n message: classification.message,\n ...(classification.data === undefined\n ? {}\n : { data: classification.data }),\n },\n classification.httpStatus,\n );\n }\n if (\n classification.kind !== 'modern' ||\n classification.classification.revision !== '2026-07-28'\n ) {\n return null;\n }\n\n // The SDK normally performs this modern HTTP routing validation before\n // dispatch. Task responses are intercepted ahead of that SDK handler, so\n // retain the same fail-closed header/body contract here.\n const headerMismatch = taskHeaderMismatch(request, body.method, params);\n if (headerMismatch) {\n return jsonRpcResponse(body.id, undefined, headerMismatch, 400);\n }\n\n const clientCapabilities = asRecord(params._meta)[\n 'io.modelcontextprotocol/clientCapabilities'\n ];\n const clientSupportsTasks = Object.hasOwn(\n asRecord(asRecord(clientCapabilities).extensions),\n MCP_TASKS_EXTENSION,\n );\n\n // A synchronous fallback exists for tools/call; lifecycle methods do not.\n if (!clientSupportsTasks && body.method === 'tools/call') return null;\n if (!clientSupportsTasks) {\n return jsonRpcResponse(\n body.id,\n undefined,\n {\n code: -32021,\n message: 'Missing required client capability',\n data: {\n requiredCapabilities: { extensions: { [MCP_TASKS_EXTENSION]: {} } },\n },\n },\n 400,\n );\n }\n\n try {\n if (body.method === 'tools/call') {\n const result = await server.callTask({\n name: params.name as string,\n arguments: (params.arguments as Record<string, unknown>) ?? {},\n principal,\n });\n return jsonRpcResponse(body.id, result);\n }\n if (typeof params.taskId !== 'string') {\n return jsonRpcResponse(body.id, undefined, {\n code: -32602,\n message: 'taskId is required',\n });\n }\n if (body.method === 'tasks/get') {\n const task = await server.getTask({ taskId: params.taskId, principal });\n return jsonRpcResponse(body.id, { resultType: 'complete', ...task });\n }\n if (body.method === 'tasks/update') {\n await server.updateTask({\n taskId: params.taskId,\n inputResponses:\n params.inputResponses && typeof params.inputResponses === 'object'\n ? (params.inputResponses as Record<string, unknown>)\n : {},\n principal,\n });\n return jsonRpcResponse(body.id, { resultType: 'complete' });\n }\n await server.cancelTask({ taskId: params.taskId, principal });\n return jsonRpcResponse(body.id, { resultType: 'complete' });\n } catch (error) {\n if (error instanceof McpAccessError) {\n const { code, retryable } = error.metadata;\n return jsonRpcResponse(body.id, undefined, {\n code: error.status === 404 ? -32602 : -32600,\n message: error.message,\n data: {\n ...(typeof code === 'string' ? { code } : {}),\n ...(typeof retryable === 'boolean' ? { retryable } : {}),\n },\n });\n }\n const message =\n error instanceof Error ? error.message : 'Task operation failed';\n return jsonRpcResponse(body.id, undefined, { code: -32602, message });\n }\n}\n\nfunction taskHeaderMismatch(\n request: Request,\n method: string | undefined,\n params: Record<string, unknown>,\n): { code: number; message: string } | undefined {\n if (\n !method ||\n normalizeHeaderValue(request.headers.get('mcp-method') ?? '') !== method\n ) {\n return {\n code: -32020,\n message: 'Mcp-Method header must match the JSON-RPC method.',\n };\n }\n const toolName =\n method === 'tools/call' && typeof params.name === 'string'\n ? params.name\n : undefined;\n const headerName = request.headers.get('mcp-name');\n if (\n toolName !== undefined &&\n (headerName === null || decodeMcpHeaderValue(headerName) !== toolName)\n ) {\n return {\n code: -32020,\n message: 'Mcp-Name header must match the tools/call name.',\n };\n }\n return undefined;\n}\n\n/** Match the SDK's RFC 9110 optional-whitespace handling for MCP headers. */\nfunction normalizeHeaderValue(value: string): string {\n return value.replace(/^[\\t ]+|[\\t ]+$/g, '');\n}\n\n/** Decode the SDK's canonical Base64 sentinel for an MCP header value. */\nfunction decodeMcpHeaderValue(value: string): string | undefined {\n const normalized = normalizeHeaderValue(value);\n const prefix = '=?base64?';\n if (!normalized.startsWith(prefix) || !normalized.endsWith('?=')) {\n return normalized;\n }\n const encoded = normalized.slice(prefix.length, -2);\n if (\n !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(\n encoded,\n )\n ) {\n return undefined;\n }\n try {\n return new TextDecoder('utf-8', { fatal: true }).decode(\n Uint8Array.from(atob(encoded), (character) => character.charCodeAt(0)),\n );\n } catch {\n return undefined;\n }\n}\n\nfunction jsonRpcResponse(\n id: string | number | null,\n result?: unknown,\n error?: { code: number; message: string; data?: unknown },\n status = 200,\n): Response {\n return new Response(\n JSON.stringify({ jsonrpc: '2.0', id, ...(error ? { error } : { result }) }),\n { status, headers: { 'content-type': 'application/json' } },\n );\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n}\n\n/**\n * Mount `server.listTools` as a `GET` handler. Returns the tool list shape\n * `{ tools }` for compatibility with the stock MCP bridge.\n *\n * @deprecated Use {@link mountMcpRoute}. Existing REST-shaped mounts can use\n * this compatibility alias while migrating without a coordinated cutover.\n */\nexport function mountMcpToolsRoute(\n server: McpAppServer,\n options: MountMcpRouteOptions = {},\n): McpSvelteKitHandler {\n return async (event) => {\n try {\n const tools = await server.listTools(\n listToolsInput(resolveRequestPrincipal(event, options)),\n );\n return jsonResponse({ tools });\n } catch (error) {\n if (error instanceof McpAccessError) {\n return jsonResponse(mcpAccessErrorBody(error), error.status);\n }\n throw error;\n }\n };\n}\n\n/**\n * Mount `server.callTool` as a `POST` handler that expects\n * `{ name, arguments }` in the JSON body.\n *\n * @deprecated Use {@link mountMcpRoute}. Existing REST-shaped mounts can use\n * this compatibility alias while migrating without a coordinated cutover.\n */\nexport function mountMcpCallRoute(\n server: McpAppServer,\n options: MountMcpRouteOptions = {},\n): McpSvelteKitHandler {\n return async (event) => {\n const body = (await event.request.json().catch(() => null)) as {\n arguments?: Record<string, unknown>;\n name?: string;\n } | null;\n\n if (!body?.name) {\n return jsonResponse({ error: 'name is required.' }, 400);\n }\n\n const input: CallToolInput = {\n arguments: body.arguments ?? {},\n name: body.name,\n principal: resolveRequestPrincipal(event, options).principal,\n };\n\n try {\n return jsonResponse(await server.callTool(input));\n } catch (error) {\n if (error instanceof McpAccessError) {\n return jsonResponse(mcpAccessErrorBody(error), error.status);\n }\n throw error;\n }\n };\n}\n\nfunction mcpAccessErrorBody(error: McpAccessError): unknown {\n const { code, retryable } = error.metadata;\n // Preserve the legacy shape unless an error deliberately opts into the\n // shared structured failure contract.\n if (!code) return { error: error.message };\n return {\n error: {\n ok: false,\n code,\n message: error.message,\n status: error.status,\n ...(retryable === undefined ? {} : { retryable }),\n },\n };\n}\n\nfunction jsonResponse(body: unknown, status = 200): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'content-type': 'application/json' },\n });\n}\n\nexport type {\n CreateDefaultMcpAppServerOptions,\n McpAppModel,\n} from './defaults.js';\nexport { McpAccessError } from './errors.js';\nexport type { McpAppPrincipal, McpAppServer } from './server.js';\n"],"mappings":"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"}
|
package/dist/workflow-tools.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** Explicit application workflow tools composed into an app MCP catalog. */
|
|
2
2
|
import { type MCPResponse, type MCPTool, type MCPToolIcon, type ToolEffect, type ToolJsonSchema } from '@happyvertical/smrt-core/generators/mcp';
|
|
3
|
+
import type { McpToolErrorMapper } from './errors.js';
|
|
3
4
|
import type { McpAppPrincipal } from './server.js';
|
|
4
5
|
/** Trusted server context supplied to an application-owned workflow handler. */
|
|
5
6
|
export interface McpWorkflowToolContext {
|
|
@@ -31,10 +32,17 @@ export interface McpWorkflowToolDefinition {
|
|
|
31
32
|
/** Preserved extension metadata. It is never used as an authorization input. */
|
|
32
33
|
metadata?: Record<string, unknown>;
|
|
33
34
|
execute(context: McpWorkflowToolContext): MCPResponse | Promise<MCPResponse>;
|
|
35
|
+
/**
|
|
36
|
+
* Map a handler failure to a safe `{ code, message }` for the agent. A
|
|
37
|
+
* thrown `McpToolError` is already safe and wins; other errors reach this
|
|
38
|
+
* mapper. Return nothing (or throw) to keep the generic failure message.
|
|
39
|
+
*/
|
|
40
|
+
mapError?: McpToolErrorMapper;
|
|
34
41
|
}
|
|
35
42
|
export interface McpWorkflowTool {
|
|
36
43
|
tool: MCPTool;
|
|
37
44
|
execute: McpWorkflowToolDefinition['execute'];
|
|
45
|
+
mapError?: McpToolErrorMapper;
|
|
38
46
|
}
|
|
39
47
|
/** Validate and materialize an authored workflow as an inert MCP descriptor. */
|
|
40
48
|
export declare function createMcpWorkflowTool(definition: McpWorkflowToolDefinition): McpWorkflowTool;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-tools.d.ts","sourceRoot":"","sources":["../src/workflow-tools.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,OAAO,EACZ,KAAK,WAAW,EAEhB,KAAK,UAAU,EACf,KAAK,cAAc,EACpB,MAAM,yCAAyC,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,gFAAgF;AAChF,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,SAAS,EAAE,eAAe,GAAG,IAAI,CAAC;CACnC;AAED,8EAA8E;AAC9E,MAAM,MAAM,yBAAyB,GAAG,KAAK,GAAG,OAAO,CAAC;AAExD,0EAA0E;AAC1E,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,cAAc,CAAC;IAC5B,YAAY,EAAE,cAAc,CAAC;IAC7B,+EAA+E;IAC/E,MAAM,EAAE,UAAU,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;IAC/B;;;OAGG;IACH,EAAE,CAAC,EAAE;QACH,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,SAAS,yBAAyB,EAAE,CAAC;KACnD,CAAC;IACF,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,OAAO,CAAC,OAAO,EAAE,sBAAsB,GAAG,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"workflow-tools.d.ts","sourceRoot":"","sources":["../src/workflow-tools.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,OAAO,EACZ,KAAK,WAAW,EAEhB,KAAK,UAAU,EACf,KAAK,cAAc,EACpB,MAAM,yCAAyC,CAAC;AACjD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,gFAAgF;AAChF,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,SAAS,EAAE,eAAe,GAAG,IAAI,CAAC;CACnC;AAED,8EAA8E;AAC9E,MAAM,MAAM,yBAAyB,GAAG,KAAK,GAAG,OAAO,CAAC;AAExD,0EAA0E;AAC1E,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,cAAc,CAAC;IAC5B,YAAY,EAAE,cAAc,CAAC;IAC7B,+EAA+E;IAC/E,MAAM,EAAE,UAAU,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;IAC/B;;;OAGG;IACH,EAAE,CAAC,EAAE;QACH,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,SAAS,yBAAyB,EAAE,CAAC;KACnD,CAAC;IACF,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,OAAO,CAAC,OAAO,EAAE,sBAAsB,GAAG,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAC7E;;;;OAIG;IACH,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC/B;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,yBAAyB,CAAC,SAAS,CAAC,CAAC;IAC9C,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC/B;AAwKD,gFAAgF;AAChF,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,yBAAyB,GACpC,eAAe,CAuGjB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-app-mcp",
|
|
3
|
-
"version": "0.57.
|
|
3
|
+
"version": "0.57.44",
|
|
4
4
|
"description": "App-runtime MCP server scaffolding for SMRT apps — `createMcpAppServer` plus transport adapters (SvelteKit today) for exposing a SMRT app's MCP surface over HTTP.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
"./auth": {
|
|
18
18
|
"types": "./dist/auth.d.ts",
|
|
19
19
|
"import": "./dist/auth.js"
|
|
20
|
+
},
|
|
21
|
+
"./host-operations": {
|
|
22
|
+
"types": "./dist/host-operations.d.ts",
|
|
23
|
+
"import": "./dist/host-operations.js"
|
|
20
24
|
}
|
|
21
25
|
},
|
|
22
26
|
"files": [
|
|
@@ -47,8 +51,8 @@
|
|
|
47
51
|
],
|
|
48
52
|
"author": "HappyVertical",
|
|
49
53
|
"dependencies": {
|
|
50
|
-
"@happyvertical/smrt-core": "0.57.
|
|
51
|
-
"@happyvertical/smrt-jobs": "0.57.
|
|
54
|
+
"@happyvertical/smrt-core": "0.57.44",
|
|
55
|
+
"@happyvertical/smrt-jobs": "0.57.44",
|
|
52
56
|
"@happyvertical/sql": "^0.111.0",
|
|
53
57
|
"@modelcontextprotocol/server": "2.3.1",
|
|
54
58
|
"jose": "^6.2.12",
|