@happyvertical/smrt-app-mcp 0.51.37 → 0.51.39
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 +3 -2
- package/README.md +113 -3
- package/dist/auth.d.ts +53 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +815 -0
- package/dist/auth.js.map +1 -0
- package/dist/chunks/protocol-Ce0U_XSp.js +184 -0
- package/dist/chunks/protocol-Ce0U_XSp.js.map +1 -0
- package/dist/continuation.d.ts +30 -0
- package/dist/continuation.d.ts.map +1 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7268 -44
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/protocol.d.ts +12 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/resources.d.ts +56 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/server.d.ts +19 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +8 -5
- package/dist/sveltekit.d.ts +3 -0
- package/dist/sveltekit.d.ts.map +1 -1
- package/dist/sveltekit.js +6 -2
- package/dist/sveltekit.js.map +1 -1
- package/dist/tools.d.ts +11 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/workflow-tools.d.ts +41 -0
- package/dist/workflow-tools.d.ts.map +1 -0
- package/package.json +10 -4
- package/dist/chunks/protocol-DoieND6v.js +0 -99
- package/dist/chunks/protocol-DoieND6v.js.map +0 -1
package/dist/manifest.json
CHANGED
package/dist/protocol.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** MCP SDK v2 protocol adapter for the framework-neutral app server core. */
|
|
2
|
+
import type { MCPTool } from '@happyvertical/smrt-core/generators/mcp';
|
|
2
3
|
import { Server, type ServerContext } from '@modelcontextprotocol/server';
|
|
3
4
|
import type { McpAppPrincipal, McpAppServer } from './server.js';
|
|
4
5
|
export declare const MCP_TASKS_EXTENSION = "io.modelcontextprotocol/tasks";
|
|
@@ -6,6 +7,17 @@ export interface McpProtocolServerOptions {
|
|
|
6
7
|
/** Resolve the authenticated application principal for each MCP request. */
|
|
7
8
|
principal?: McpAppPrincipal | null | ((context: ServerContext) => McpAppPrincipal | null | Promise<McpAppPrincipal | null>);
|
|
8
9
|
}
|
|
10
|
+
/** Request-bound options for asynchronous catalog extension projection. */
|
|
11
|
+
export interface McpProtocolRequestOptions {
|
|
12
|
+
principal?: McpAppPrincipal | null;
|
|
13
|
+
/** Optional inert extension metadata, recomputed from this request's authorized catalog. */
|
|
14
|
+
extensions?: (context: {
|
|
15
|
+
principal: McpAppPrincipal | null;
|
|
16
|
+
tools: readonly MCPTool[];
|
|
17
|
+
}) => Record<string, Record<string, unknown>> | Promise<Record<string, Record<string, unknown>>>;
|
|
18
|
+
}
|
|
19
|
+
/** Prepare native capabilities before the SDK HTTP factory installs discovery handlers. */
|
|
20
|
+
export declare function createMcpProtocolServerForRequest(appServer: McpAppServer, options?: McpProtocolRequestOptions): Promise<Server>;
|
|
9
21
|
/**
|
|
10
22
|
* Adapt an app MCP core to the SDK v2 low-level server protocol.
|
|
11
23
|
*
|
package/dist/protocol.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,6EAA6E;
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAE7E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AACvE,OAAO,EAKL,MAAM,EACN,KAAK,aAAa,EAEnB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGjE,eAAO,MAAM,mBAAmB,kCAAkC,CAAC;AAOnE,MAAM,WAAW,wBAAwB;IACvC,4EAA4E;IAC5E,SAAS,CAAC,EACN,eAAe,GACf,IAAI,GACJ,CAAC,CACC,OAAO,EAAE,aAAa,KACnB,eAAe,GAAG,IAAI,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,CAAC;CACpE;AAED,2EAA2E;AAC3E,MAAM,WAAW,yBAAyB;IACxC,SAAS,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACnC,4FAA4F;IAC5F,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE;QACrB,SAAS,EAAE,eAAe,GAAG,IAAI,CAAC;QAClC,KAAK,EAAE,SAAS,OAAO,EAAE,CAAC;KAC3B,KACG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACvC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;CACtD;AAED,2FAA2F;AAC3F,wBAAsB,iCAAiC,CACrD,SAAS,EAAE,YAAY,EACvB,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,MAAM,CAAC,CAmBjB;AAUD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,YAAY,EACvB,OAAO,GAAE,wBAA6B,GACrC,MAAM,CAOR"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { McpAppPrincipal } from './server.js';
|
|
2
|
+
export declare const MCP_APP_RESOURCE_MIME = "text/html;profile=mcp-app";
|
|
3
|
+
export declare const MCP_APP_RESOURCE_MAX_BYTES: number;
|
|
4
|
+
export interface McpAppResourceCsp {
|
|
5
|
+
connectDomains?: readonly string[];
|
|
6
|
+
resourceDomains?: readonly string[];
|
|
7
|
+
frameDomains?: readonly string[];
|
|
8
|
+
baseUriDomains?: readonly string[];
|
|
9
|
+
}
|
|
10
|
+
export interface McpAppResourceDefinition {
|
|
11
|
+
/** Absolute ui:// URI containing the declared version as a path segment. */
|
|
12
|
+
uri: string;
|
|
13
|
+
name: string;
|
|
14
|
+
version: string;
|
|
15
|
+
/** Reproducibly built UTF-8 HTML; never per-user data or credentials. */
|
|
16
|
+
html: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
/** Bounded JSON extension metadata; portable ui and digest keys are reserved. */
|
|
19
|
+
metadata?: Record<string, unknown>;
|
|
20
|
+
/** Only explicit static templates may be read without authentication. */
|
|
21
|
+
public?: boolean;
|
|
22
|
+
csp?: McpAppResourceCsp;
|
|
23
|
+
permissions?: Partial<Record<'camera' | 'microphone' | 'geolocation' | 'clipboardWrite', Record<string, never>>>;
|
|
24
|
+
}
|
|
25
|
+
export interface McpAppResource {
|
|
26
|
+
uri: string;
|
|
27
|
+
name: string;
|
|
28
|
+
description?: string;
|
|
29
|
+
mimeType: typeof MCP_APP_RESOURCE_MIME;
|
|
30
|
+
_meta: Record<string, unknown> & {
|
|
31
|
+
ui: {
|
|
32
|
+
csp: Required<McpAppResourceCsp>;
|
|
33
|
+
permissions: NonNullable<McpAppResourceDefinition['permissions']>;
|
|
34
|
+
};
|
|
35
|
+
'com.happyvertical.smrt/resource': {
|
|
36
|
+
version: string;
|
|
37
|
+
sha256: string;
|
|
38
|
+
bytes: number;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export interface McpAppResourceContent extends McpAppResource {
|
|
43
|
+
text: string;
|
|
44
|
+
}
|
|
45
|
+
export type McpResourcePolicy = (context: {
|
|
46
|
+
principal: McpAppPrincipal | null;
|
|
47
|
+
resource: McpAppResource;
|
|
48
|
+
}) => boolean | Promise<boolean>;
|
|
49
|
+
export interface PreparedMcpAppResource {
|
|
50
|
+
descriptor: McpAppResource;
|
|
51
|
+
html: string;
|
|
52
|
+
public: boolean;
|
|
53
|
+
}
|
|
54
|
+
/** Snapshot and validate a deterministic declaration at application startup/build. */
|
|
55
|
+
export declare function prepareMcpAppResource(definition: McpAppResourceDefinition): PreparedMcpAppResource;
|
|
56
|
+
//# sourceMappingURL=resources.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,eAAO,MAAM,qBAAqB,8BAA8B,CAAC;AACjE,eAAO,MAAM,0BAA0B,QAAa,CAAC;AACrD,MAAM,WAAW,iBAAiB;IAChC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AACD,MAAM,WAAW,wBAAwB;IACvC,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,yEAAyE;IACzE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,iBAAiB,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CACnB,MAAM,CACJ,QAAQ,GAAG,YAAY,GAAG,aAAa,GAAG,gBAAgB,EAC1D,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CACtB,CACF,CAAC;CACH;AACD,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,OAAO,qBAAqB,CAAC;IACvC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;QAC/B,EAAE,EAAE;YACF,GAAG,EAAE,QAAQ,CAAC,iBAAiB,CAAC,CAAC;YACjC,WAAW,EAAE,WAAW,CAAC,wBAAwB,CAAC,aAAa,CAAC,CAAC,CAAC;SACnE,CAAC;QACF,iCAAiC,EAAE;YACjC,OAAO,EAAE,MAAM,CAAC;YAChB,MAAM,EAAE,MAAM,CAAC;YACf,KAAK,EAAE,MAAM,CAAC;SACf,CAAC;KACH,CAAC;CACH;AACD,MAAM,WAAW,qBAAsB,SAAQ,cAAc;IAC3D,IAAI,EAAE,MAAM,CAAC;CACd;AACD,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAE;IACxC,SAAS,EAAE,eAAe,GAAG,IAAI,CAAC;IAClC,QAAQ,EAAE,cAAc,CAAC;CAC1B,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AACjC,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,cAAc,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CACjB;AAyKD,sFAAsF;AACtF,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,wBAAwB,GACnC,sBAAsB,CAgGxB"}
|
package/dist/server.d.ts
CHANGED
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import type { MCPConfig, MCPResponse, MCPTool } from '@happyvertical/smrt-core/generators/mcp';
|
|
19
19
|
import { type McpTask } from '@happyvertical/smrt-jobs';
|
|
20
|
+
import { type McpAppResource, type McpAppResourceContent, type McpAppResourceDefinition, type McpResourcePolicy } from './resources.js';
|
|
21
|
+
import { type McpWorkflowToolDefinition } from './workflow-tools.js';
|
|
20
22
|
/**
|
|
21
23
|
* Generic authenticated caller information available to app-MCP policy.
|
|
22
24
|
*
|
|
@@ -119,6 +121,16 @@ export interface CreateMcpAppServerOptions {
|
|
|
119
121
|
* may mutate `args` to inject trusted fields.
|
|
120
122
|
*/
|
|
121
123
|
workflowAssertions?: Record<string, McpWorkflowAssertion>;
|
|
124
|
+
/**
|
|
125
|
+
* Explicit application workflows composed with the generated catalog. Their
|
|
126
|
+
* descriptors use core's canonical capability classification and every call
|
|
127
|
+
* passes through this server's principal, tenant, allow-list policy, and
|
|
128
|
+
* assertion gates before the handler runs.
|
|
129
|
+
*/
|
|
130
|
+
workflowTools?: readonly McpWorkflowToolDefinition[];
|
|
131
|
+
resources?: readonly McpAppResourceDefinition[];
|
|
132
|
+
/** Required for private resources; rechecked on catalog/read, errors deny. */
|
|
133
|
+
resourcePolicy?: McpResourcePolicy;
|
|
122
134
|
}
|
|
123
135
|
/** Tool listing inputs. */
|
|
124
136
|
export interface ListToolsInput {
|
|
@@ -145,6 +157,13 @@ export interface CallToolInput {
|
|
|
145
157
|
/** Shape returned by `createMcpAppServer`. */
|
|
146
158
|
export interface McpAppServer {
|
|
147
159
|
listTools(input: ListToolsInput): Promise<MCPTool[]>;
|
|
160
|
+
listResources?(input: {
|
|
161
|
+
principal?: McpAppPrincipal | null;
|
|
162
|
+
}): Promise<McpAppResource[]>;
|
|
163
|
+
readResource?(input: {
|
|
164
|
+
uri: string;
|
|
165
|
+
principal?: McpAppPrincipal | null;
|
|
166
|
+
}): Promise<McpAppResourceContent>;
|
|
148
167
|
callTool(input: CallToolInput): Promise<MCPResponse>;
|
|
149
168
|
/** Whether this app has any explicitly enabled Tasks extension action. */
|
|
150
169
|
hasTaskSupport?(): Promise<boolean>;
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,OAAO,KAAK,EACV,SAAS,EACT,WAAW,EACX,OAAO,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/smrt-knowledge.json
CHANGED
|
@@ -3,16 +3,17 @@
|
|
|
3
3
|
"sensitiveFieldsExcluded": true,
|
|
4
4
|
"generatedAt": "1970-01-01T00:00:00.000Z",
|
|
5
5
|
"packageName": "@happyvertical/smrt-app-mcp",
|
|
6
|
-
"packageVersion": "0.51.
|
|
6
|
+
"packageVersion": "0.51.39",
|
|
7
7
|
"sourceManifestPath": "dist/manifest.json",
|
|
8
8
|
"agentDocPath": "AGENTS.md",
|
|
9
9
|
"sourceHashes": {
|
|
10
|
-
"manifest": "
|
|
11
|
-
"packageJson": "
|
|
12
|
-
"agents": "
|
|
10
|
+
"manifest": "f72a56dc0b1c45b7880700fa11b2c5f5840f2fb0c34b7ab1ec76764dd221451f",
|
|
11
|
+
"packageJson": "a828fb124d3e53c36b2b82ffa6cd3d478916fabf7ee752378b79dbb1ce1c46a3",
|
|
12
|
+
"agents": "f249d4732daeb08f865b67237c0da2b166a96f749b1ec6b595f2bb17c24c2d30"
|
|
13
13
|
},
|
|
14
14
|
"exports": [
|
|
15
15
|
".",
|
|
16
|
+
"./auth",
|
|
16
17
|
"./sveltekit"
|
|
17
18
|
],
|
|
18
19
|
"dependencies": {
|
|
@@ -20,6 +21,8 @@
|
|
|
20
21
|
"@happyvertical/smrt-jobs": "workspace:*",
|
|
21
22
|
"@happyvertical/sql": "catalog:",
|
|
22
23
|
"@modelcontextprotocol/server": "2.0.0",
|
|
24
|
+
"jose": "^6.2.12",
|
|
25
|
+
"parse5": "8.0.1",
|
|
23
26
|
"@modelcontextprotocol/client": "2.0.0",
|
|
24
27
|
"@modelcontextprotocol/conformance": "0.2.0-alpha.11",
|
|
25
28
|
"@modelcontextprotocol/node": "2.0.0",
|
|
@@ -56,5 +59,5 @@
|
|
|
56
59
|
"polymorphicAssociations": 0,
|
|
57
60
|
"uuidColumns": 0
|
|
58
61
|
},
|
|
59
|
-
"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, then filter by allowed class-name prefixes.\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
|
|
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, then filter by allowed class-name prefixes.\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- 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"
|
|
60
63
|
}
|
package/dist/sveltekit.d.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* export const POST = mountMcpRoute(mcpServer);
|
|
14
14
|
* ```
|
|
15
15
|
*/
|
|
16
|
+
import { type McpProtocolRequestOptions } from './protocol.js';
|
|
16
17
|
import type { McpAppPrincipal, McpAppServer } from './server.js';
|
|
17
18
|
/** Minimal subset of a SvelteKit RequestEvent we actually touch. */
|
|
18
19
|
type SvelteKitRequestEvent = {
|
|
@@ -28,6 +29,8 @@ export type McpPrincipalResolver = (event: SvelteKitRequestEvent) => McpAppPrinc
|
|
|
28
29
|
export type McpUserResolver = McpPrincipalResolver;
|
|
29
30
|
/** Options shared by both route mounts. */
|
|
30
31
|
export interface MountMcpRouteOptions {
|
|
32
|
+
/** Optional extension discovery projected from the request-authorized tool catalog. */
|
|
33
|
+
extensions?: McpProtocolRequestOptions['extensions'];
|
|
31
34
|
/**
|
|
32
35
|
* Resolve the request principal once for both discovery and direct calls.
|
|
33
36
|
* Defaults to `event.locals.user`.
|
package/dist/sveltekit.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sveltekit.d.ts","sourceRoot":"","sources":["../src/sveltekit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;
|
|
1
|
+
{"version":3,"file":"sveltekit.d.ts","sourceRoot":"","sources":["../src/sveltekit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAQH,OAAO,EAGL,KAAK,yBAAyB,EAC/B,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAiB,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhF,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,2CAA2C;AAC3C,MAAM,WAAW,oBAAoB;IACnC,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,CA8BrB;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,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,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { f as McpAccessError, r as createMcpProtocolServerForRequest, t as MCP_TASKS_EXTENSION } from "./chunks/protocol-Ce0U_XSp.js";
|
|
2
2
|
import { classifyInboundRequest, createMcpHandler, isJsonContentType } from "@modelcontextprotocol/server";
|
|
3
3
|
//#region src/sveltekit.ts
|
|
4
4
|
var defaultResolvePrincipal = (event) => event.locals?.user ?? null;
|
|
@@ -20,6 +20,7 @@ function listToolsInput(resolved) {
|
|
|
20
20
|
function protocolServerForRequest(server, resolved) {
|
|
21
21
|
if (!resolved.legacyAuthenticated || resolved.principal) return server;
|
|
22
22
|
return {
|
|
23
|
+
...server,
|
|
23
24
|
serverInfo: server.serverInfo,
|
|
24
25
|
listTools: () => server.listTools({ authenticated: true }),
|
|
25
26
|
callTool: (input) => server.callTool(input)
|
|
@@ -30,7 +31,10 @@ function mountMcpRoute(server, options = {}) {
|
|
|
30
31
|
const resolved = resolveRequestPrincipal(event, options);
|
|
31
32
|
const taskResponse = await maybeHandleTaskRequest(server, resolved.principal, event.request);
|
|
32
33
|
if (taskResponse) return taskResponse;
|
|
33
|
-
return createMcpHandler(() =>
|
|
34
|
+
return createMcpHandler(() => createMcpProtocolServerForRequest(protocolServerForRequest(server, resolved), {
|
|
35
|
+
principal: resolved.principal,
|
|
36
|
+
extensions: options.extensions
|
|
37
|
+
}), {
|
|
34
38
|
legacy: "reject",
|
|
35
39
|
maxSubscriptions: 0
|
|
36
40
|
}).fetch(event.request);
|
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\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 {\n classifyInboundRequest,\n createMcpHandler,\n isJsonContentType,\n} from '@modelcontextprotocol/server';\nimport { McpAccessError } from './errors.js';\nimport { createMcpProtocolServer, MCP_TASKS_EXTENSION } from './protocol.js';\nimport type { CallToolInput, McpAppPrincipal, McpAppServer } 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/** Options shared by both route mounts. */\nexport interface MountMcpRouteOptions {\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 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 async (event) => {\n const resolved = resolveRequestPrincipal(event, options);\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 createMcpProtocolServer(protocolServerForRequest(server, resolved), {\n principal: resolved.principal,\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 tools-only 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\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 { McpAccessError } from './errors.js';\nexport type { McpAppPrincipal, McpAppServer } from './server.js';\n"],"mappings":"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"}
|
|
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\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 {\n classifyInboundRequest,\n createMcpHandler,\n isJsonContentType,\n} from '@modelcontextprotocol/server';\nimport { McpAccessError } from './errors.js';\nimport {\n createMcpProtocolServerForRequest,\n MCP_TASKS_EXTENSION,\n type McpProtocolRequestOptions,\n} from './protocol.js';\nimport type { CallToolInput, McpAppPrincipal, McpAppServer } 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/** Options shared by both route mounts. */\nexport interface MountMcpRouteOptions {\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 async (event) => {\n const resolved = resolveRequestPrincipal(event, options);\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\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 { McpAccessError } from './errors.js';\nexport type { McpAppPrincipal, McpAppServer } from './server.js';\n"],"mappings":"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"}
|
package/dist/tools.d.ts
CHANGED
|
@@ -29,6 +29,17 @@ export declare function isReadOnlyToolName(toolName: string): boolean;
|
|
|
29
29
|
* be public, regardless of pattern.
|
|
30
30
|
*/
|
|
31
31
|
export declare function isPublicToolName(toolName: string, patterns: readonly string[]): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Check public eligibility using a descriptor's canonical read annotation when
|
|
34
|
+
* available. The legacy name rule remains for generated catalogs predating
|
|
35
|
+
* canonical annotations.
|
|
36
|
+
*/
|
|
37
|
+
export declare function isPublicMcpTool(tool: {
|
|
38
|
+
name: string;
|
|
39
|
+
annotations?: {
|
|
40
|
+
readOnlyHint?: boolean;
|
|
41
|
+
};
|
|
42
|
+
}, patterns: readonly string[]): boolean;
|
|
32
43
|
/**
|
|
33
44
|
* Lower-case `<class>_` prefixes the app considers "allowed core tools"
|
|
34
45
|
* given a list of SMRT class names. Used to build the allow-list for
|
package/dist/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAkB7E;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B,OAAO,CAKT;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,SAAS,MAAM,EAAE,GAC5B,WAAW,CAAC,MAAM,CAAC,CAErB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,GAC5B,OAAO,CAKT;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvE"}
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAkB7E;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B,OAAO,CAKT;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE;IACJ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC1C,EACD,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B,OAAO,CAOT;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,SAAS,MAAM,EAAE,GAC5B,WAAW,CAAC,MAAM,CAAC,CAErB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,GAC5B,OAAO,CAKT;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvE"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** Explicit application workflow tools composed into an app MCP catalog. */
|
|
2
|
+
import { type MCPResponse, type MCPTool, type MCPToolIcon, type ToolEffect, type ToolJsonSchema } from '@happyvertical/smrt-core/generators/mcp';
|
|
3
|
+
import type { McpAppPrincipal } from './server.js';
|
|
4
|
+
/** Trusted server context supplied to an application-owned workflow handler. */
|
|
5
|
+
export interface McpWorkflowToolContext {
|
|
6
|
+
arguments: Record<string, unknown>;
|
|
7
|
+
principal: McpAppPrincipal | null;
|
|
8
|
+
}
|
|
9
|
+
/** Portable UI visibility hints. They are host presentation metadata only. */
|
|
10
|
+
export type McpWorkflowToolVisibility = 'app' | 'model';
|
|
11
|
+
/** One explicitly declared, principal-bound application workflow tool. */
|
|
12
|
+
export interface McpWorkflowToolDefinition {
|
|
13
|
+
name: string;
|
|
14
|
+
description: string;
|
|
15
|
+
inputSchema: ToolJsonSchema;
|
|
16
|
+
outputSchema: ToolJsonSchema;
|
|
17
|
+
/** Required canonical classification; names and UI metadata never infer it. */
|
|
18
|
+
effect: ToolEffect;
|
|
19
|
+
idempotent: boolean;
|
|
20
|
+
openWorld: boolean;
|
|
21
|
+
title?: string;
|
|
22
|
+
icons?: readonly MCPToolIcon[];
|
|
23
|
+
/**
|
|
24
|
+
* Portable MCP Apps presentation metadata. Resources themselves arrive in
|
|
25
|
+
* M3; visibility-only declarations are useful for host entrypoints.
|
|
26
|
+
*/
|
|
27
|
+
ui?: {
|
|
28
|
+
resourceUri?: string;
|
|
29
|
+
visibility?: readonly McpWorkflowToolVisibility[];
|
|
30
|
+
};
|
|
31
|
+
/** Preserved extension metadata. It is never used as an authorization input. */
|
|
32
|
+
metadata?: Record<string, unknown>;
|
|
33
|
+
execute(context: McpWorkflowToolContext): MCPResponse | Promise<MCPResponse>;
|
|
34
|
+
}
|
|
35
|
+
export interface McpWorkflowTool {
|
|
36
|
+
tool: MCPTool;
|
|
37
|
+
execute: McpWorkflowToolDefinition['execute'];
|
|
38
|
+
}
|
|
39
|
+
/** Validate and materialize an authored workflow as an inert MCP descriptor. */
|
|
40
|
+
export declare function createMcpWorkflowTool(definition: McpWorkflowToolDefinition): McpWorkflowTool;
|
|
41
|
+
//# sourceMappingURL=workflow-tools.d.ts.map
|
|
@@ -0,0 +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;CAC9E;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,yBAAyB,CAAC,SAAS,CAAC,CAAC;CAC/C;AAwKD,gFAAgF;AAChF,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,yBAAyB,GACpC,eAAe,CAgGjB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-app-mcp",
|
|
3
|
-
"version": "0.51.
|
|
3
|
+
"version": "0.51.39",
|
|
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",
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
"./sveltekit": {
|
|
14
14
|
"types": "./dist/sveltekit.d.ts",
|
|
15
15
|
"import": "./dist/sveltekit.js"
|
|
16
|
+
},
|
|
17
|
+
"./auth": {
|
|
18
|
+
"types": "./dist/auth.d.ts",
|
|
19
|
+
"import": "./dist/auth.js"
|
|
16
20
|
}
|
|
17
21
|
},
|
|
18
22
|
"files": [
|
|
@@ -43,10 +47,12 @@
|
|
|
43
47
|
],
|
|
44
48
|
"author": "HappyVertical",
|
|
45
49
|
"dependencies": {
|
|
46
|
-
"@happyvertical/smrt-core": "0.51.
|
|
47
|
-
"@happyvertical/smrt-jobs": "0.51.
|
|
50
|
+
"@happyvertical/smrt-core": "0.51.39",
|
|
51
|
+
"@happyvertical/smrt-jobs": "0.51.39",
|
|
48
52
|
"@happyvertical/sql": "^0.96.0",
|
|
49
|
-
"@modelcontextprotocol/server": "2.0.0"
|
|
53
|
+
"@modelcontextprotocol/server": "2.0.0",
|
|
54
|
+
"jose": "^6.2.12",
|
|
55
|
+
"parse5": "8.0.1"
|
|
50
56
|
},
|
|
51
57
|
"devDependencies": {
|
|
52
58
|
"@modelcontextprotocol/client": "2.0.0",
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { ProtocolError, ProtocolErrorCode, Server } from "@modelcontextprotocol/server";
|
|
2
|
-
//#region src/errors.ts
|
|
3
|
-
var MCP_TOOL_ACCESS_DENIED_CODE = "mcp_tool_access_denied";
|
|
4
|
-
var McpAccessError = class extends Error {
|
|
5
|
-
constructor(status, message, metadata = {}) {
|
|
6
|
-
super(message);
|
|
7
|
-
this.status = status;
|
|
8
|
-
this.metadata = metadata;
|
|
9
|
-
this.name = "McpAccessError";
|
|
10
|
-
}
|
|
11
|
-
status;
|
|
12
|
-
metadata;
|
|
13
|
-
};
|
|
14
|
-
//#endregion
|
|
15
|
-
//#region src/tools.ts
|
|
16
|
-
function matchesToolPattern(toolName, pattern) {
|
|
17
|
-
if (!pattern) return false;
|
|
18
|
-
if (pattern === "*") return true;
|
|
19
|
-
const parts = pattern.split("*");
|
|
20
|
-
if (parts.length === 1) return toolName === pattern;
|
|
21
|
-
let cursor = 0;
|
|
22
|
-
if (parts[0] && !toolName.startsWith(parts[0])) return false;
|
|
23
|
-
for (const part of parts) {
|
|
24
|
-
if (!part) continue;
|
|
25
|
-
const index = toolName.indexOf(part, cursor);
|
|
26
|
-
if (index < 0) return false;
|
|
27
|
-
cursor = index + part.length;
|
|
28
|
-
}
|
|
29
|
-
const last = parts.at(-1);
|
|
30
|
-
return !last || toolName.endsWith(last);
|
|
31
|
-
}
|
|
32
|
-
function isReadOnlyToolName(toolName) {
|
|
33
|
-
return toolName.endsWith("_list") || toolName.endsWith("_get");
|
|
34
|
-
}
|
|
35
|
-
function isPublicToolName(toolName, patterns) {
|
|
36
|
-
return isReadOnlyToolName(toolName) && patterns.some((pattern) => matchesToolPattern(toolName, pattern));
|
|
37
|
-
}
|
|
38
|
-
function classNamePrefixes(classNames) {
|
|
39
|
-
return new Set(classNames.map((className) => `${className.toLowerCase()}_`));
|
|
40
|
-
}
|
|
41
|
-
function isAllowedCoreTool(toolName, prefixes) {
|
|
42
|
-
for (const prefix of prefixes) if (toolName.startsWith(prefix)) return true;
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
function compareMcpToolNames(left, right) {
|
|
46
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
47
|
-
}
|
|
48
|
-
//#endregion
|
|
49
|
-
//#region src/protocol.ts
|
|
50
|
-
var MCP_TASKS_EXTENSION = "io.modelcontextprotocol/tasks";
|
|
51
|
-
var DEFAULT_TOOL_LIST_CACHE_HINT = {
|
|
52
|
-
ttlMs: 864e5,
|
|
53
|
-
cacheScope: "private"
|
|
54
|
-
};
|
|
55
|
-
async function resolvePrincipal(option, context) {
|
|
56
|
-
if (typeof option === "function") return await option(context) ?? null;
|
|
57
|
-
return option ?? null;
|
|
58
|
-
}
|
|
59
|
-
function createMcpProtocolServer(appServer, options = {}) {
|
|
60
|
-
const tasksEnabled = appServer.tasksEnabled && typeof options.principal !== "function" && Boolean(options.principal?.id);
|
|
61
|
-
const server = new Server(appServer.serverInfo, {
|
|
62
|
-
capabilities: {
|
|
63
|
-
tools: {},
|
|
64
|
-
...tasksEnabled ? { extensions: { [MCP_TASKS_EXTENSION]: {} } } : {}
|
|
65
|
-
},
|
|
66
|
-
cacheHints: { "tools/list": DEFAULT_TOOL_LIST_CACHE_HINT }
|
|
67
|
-
});
|
|
68
|
-
server.setRequestHandler("tools/list", async (_request, context) => {
|
|
69
|
-
const principal = await resolvePrincipal(options.principal, context);
|
|
70
|
-
const cacheHint = await appServer.getToolsListCacheHint?.() ?? DEFAULT_TOOL_LIST_CACHE_HINT;
|
|
71
|
-
return {
|
|
72
|
-
tools: [...await appServer.listTools({ principal })].sort((left, right) => compareMcpToolNames(left.name, right.name)),
|
|
73
|
-
...cacheHint
|
|
74
|
-
};
|
|
75
|
-
});
|
|
76
|
-
server.setRequestHandler("tools/call", async (request, context) => {
|
|
77
|
-
try {
|
|
78
|
-
return await appServer.callTool({
|
|
79
|
-
name: request.params.name,
|
|
80
|
-
arguments: request.params.arguments,
|
|
81
|
-
principal: await resolvePrincipal(options.principal, context)
|
|
82
|
-
});
|
|
83
|
-
} catch (error) {
|
|
84
|
-
if (error instanceof McpAccessError) {
|
|
85
|
-
const { code, retryable } = error.metadata;
|
|
86
|
-
throw new ProtocolError(error.status === 404 ? ProtocolErrorCode.InvalidParams : ProtocolErrorCode.InvalidRequest, error.message, {
|
|
87
|
-
...typeof code === "string" ? { code } : {},
|
|
88
|
-
...typeof retryable === "boolean" ? { retryable } : {}
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
throw error;
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
return server;
|
|
95
|
-
}
|
|
96
|
-
//#endregion
|
|
97
|
-
export { isAllowedCoreTool as a, matchesToolPattern as c, compareMcpToolNames as i, MCP_TOOL_ACCESS_DENIED_CODE as l, createMcpProtocolServer as n, isPublicToolName as o, classNamePrefixes as r, isReadOnlyToolName as s, MCP_TASKS_EXTENSION as t, McpAccessError as u };
|
|
98
|
-
|
|
99
|
-
//# sourceMappingURL=protocol-DoieND6v.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"protocol-DoieND6v.js","names":[],"sources":["../../src/errors.ts","../../src/tools.ts","../../src/protocol.ts"],"sourcesContent":["/** Machine-readable code for a principal policy denial. */\nexport const MCP_TOOL_ACCESS_DENIED_CODE = 'mcp_tool_access_denied';\n\n/**\n * Metadata that is safe to expose for an app-MCP access failure. Policy\n * implementations must not place principal, scope, tool, or internal-error\n * details here.\n */\nexport interface McpAccessErrorMetadata {\n code?: string;\n retryable?: boolean;\n}\n\n/**\n * Error returned by the MCP app server when a caller tries to use a tool\n * they are not allowed to access. The HTTP layer should map `status` onto\n * the response status code.\n */\nexport class McpAccessError extends Error {\n constructor(\n readonly status: number,\n message: string,\n readonly metadata: McpAccessErrorMetadata = {},\n ) {\n super(message);\n this.name = 'McpAccessError';\n }\n}\n","/**\n * Tool-name policy helpers — used by `McpAppServer` to filter the full set\n * of generated tools down to what the calling principal is allowed to see,\n * and to decide whether an unauthenticated tool call should be permitted.\n *\n * @packageDocumentation\n */\n\n/**\n * Match a tool name against a glob-ish pattern with `*` wildcards.\n *\n * - Empty pattern → never matches.\n * - `*` → matches everything.\n * - `prefix_*` → matches anything starting with `prefix_`.\n * - `*_suffix` → matches anything ending with `_suffix`.\n * - `a_*_b` → matches any name containing `a_`, then any text, then `_b`.\n *\n * No regex characters are special besides `*` — the input is treated as a\n * literal string with star wildcards.\n */\nexport function matchesToolPattern(toolName: string, pattern: string): boolean {\n if (!pattern) return false;\n if (pattern === '*') return true;\n\n const parts = pattern.split('*');\n if (parts.length === 1) return toolName === pattern;\n\n let cursor = 0;\n if (parts[0] && !toolName.startsWith(parts[0])) return false;\n for (const part of parts) {\n if (!part) continue;\n const index = toolName.indexOf(part, cursor);\n if (index < 0) return false;\n cursor = index + part.length;\n }\n\n const last = parts.at(-1);\n return !last || toolName.endsWith(last);\n}\n\n/**\n * Read-only tool detection. Generated SMRT MCP tools follow the naming\n * convention `<class>_<verb>`; we treat `_list` and `_get` as read-only.\n */\nexport function isReadOnlyToolName(toolName: string): boolean {\n return toolName.endsWith('_list') || toolName.endsWith('_get');\n}\n\n/**\n * Check whether a tool name is currently allowed for unauthenticated callers\n * given the configured public-tool patterns. Only read-only tools may ever\n * be public, regardless of pattern.\n */\nexport function isPublicToolName(\n toolName: string,\n patterns: readonly string[],\n): boolean {\n return (\n isReadOnlyToolName(toolName) &&\n patterns.some((pattern) => matchesToolPattern(toolName, pattern))\n );\n}\n\n/**\n * Lower-case `<class>_` prefixes the app considers \"allowed core tools\"\n * given a list of SMRT class names. Used to build the allow-list for\n * `McpAppServer.listTools`.\n */\nexport function classNamePrefixes(\n classNames: readonly string[],\n): ReadonlySet<string> {\n return new Set(classNames.map((className) => `${className.toLowerCase()}_`));\n}\n\n/**\n * Whether a given tool name starts with any of the configured class\n * prefixes.\n */\nexport function isAllowedCoreTool(\n toolName: string,\n prefixes: ReadonlySet<string>,\n): boolean {\n for (const prefix of prefixes) {\n if (toolName.startsWith(prefix)) return true;\n }\n return false;\n}\n\n/**\n * Compare tool names by Unicode code unit, rather than the host locale, so a\n * catalog has one byte-stable order across every runtime.\n */\nexport function compareMcpToolNames(left: string, right: string): number {\n return left < right ? -1 : left > right ? 1 : 0;\n}\n","/** MCP SDK v2 protocol adapter for the framework-neutral app server core. */\nimport {\n type CallToolResult,\n ProtocolError,\n ProtocolErrorCode,\n Server,\n type ServerContext,\n type Tool,\n} from '@modelcontextprotocol/server';\nimport { McpAccessError } from './errors.js';\nimport type { McpAppPrincipal, McpAppServer } from './server.js';\nimport { compareMcpToolNames } from './tools.js';\n\nexport const MCP_TASKS_EXTENSION = 'io.modelcontextprotocol/tasks';\n\nconst DEFAULT_TOOL_LIST_CACHE_HINT = {\n ttlMs: 86_400_000,\n cacheScope: 'private' as const,\n};\n\nexport interface McpProtocolServerOptions {\n /** Resolve the authenticated application principal for each MCP request. */\n principal?:\n | McpAppPrincipal\n | null\n | ((\n context: ServerContext,\n ) => McpAppPrincipal | null | Promise<McpAppPrincipal | null>);\n}\n\nasync function resolvePrincipal(\n option: McpProtocolServerOptions['principal'],\n context: ServerContext,\n): Promise<McpAppPrincipal | null> {\n if (typeof option === 'function') return (await option(context)) ?? null;\n return option ?? null;\n}\n\n/**\n * Adapt an app MCP core to the SDK v2 low-level server protocol.\n *\n * Transport ownership remains with the caller. In particular, this does not\n * add a production HTTP endpoint; it is safe to compose with `serveStdio` or\n * `createMcpHandler` in a deployment that supplies its own authentication.\n */\nexport function createMcpProtocolServer(\n appServer: McpAppServer,\n options: McpProtocolServerOptions = {},\n): Server {\n // Task lifecycle records are owner-scoped. Unlike ordinary public read-only\n // tools, they cannot be safely exposed without a stable principal id.\n const tasksEnabled =\n appServer.tasksEnabled &&\n typeof options.principal !== 'function' &&\n Boolean(options.principal?.id);\n const server = new Server(appServer.serverInfo, {\n capabilities: {\n tools: {},\n ...(tasksEnabled ? { extensions: { [MCP_TASKS_EXTENSION]: {} } } : {}),\n } as never,\n cacheHints: { 'tools/list': DEFAULT_TOOL_LIST_CACHE_HINT },\n });\n\n server.setRequestHandler('tools/list', async (_request, context) => {\n const principal = await resolvePrincipal(options.principal, context);\n const cacheHint =\n (await appServer.getToolsListCacheHint?.()) ??\n DEFAULT_TOOL_LIST_CACHE_HINT;\n return {\n tools: [...(await appServer.listTools({ principal }))].sort(\n (left, right) => compareMcpToolNames(left.name, right.name),\n ) as Tool[],\n ...cacheHint,\n };\n });\n\n server.setRequestHandler('tools/call', async (request, context) => {\n try {\n return (await appServer.callTool({\n name: request.params.name,\n arguments: request.params.arguments,\n principal: await resolvePrincipal(options.principal, context),\n })) as CallToolResult;\n } catch (error) {\n if (error instanceof McpAccessError) {\n const { code, retryable } = error.metadata;\n throw new ProtocolError(\n error.status === 404\n ? ProtocolErrorCode.InvalidParams\n : ProtocolErrorCode.InvalidRequest,\n error.message,\n {\n ...(typeof code === 'string' ? { code } : {}),\n ...(typeof retryable === 'boolean' ? { retryable } : {}),\n },\n );\n }\n throw error;\n }\n });\n\n return server;\n}\n"],"mappings":"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"}
|