@happyvertical/smrt-app-mcp 0.51.36 → 0.51.38
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/AGENTS.md
CHANGED
|
@@ -17,8 +17,9 @@ tool policy; `./sveltekit` owns the stateless Streamable HTTP transport.
|
|
|
17
17
|
- Treat list/get tools as read-only; mutating tools require authentication unless the app deliberately wraps them with stronger policy.
|
|
18
18
|
- Keep app policy outside generated tool schemas. The wrapper owns auth, allow-list, and workflow assertion behavior.
|
|
19
19
|
- Return 404 for tools outside the app allow-list so private generated tools are not enumerated by mistake.
|
|
20
|
-
- Trust only a principal supplied by the application adapter. This package
|
|
21
|
-
not
|
|
20
|
+
- Trust only a principal supplied by the application adapter. This package
|
|
21
|
+
does not implement an OAuth authorization server. The opt-in `./auth` entry
|
|
22
|
+
validates JWT access tokens and requires an application-owned principal mapper.
|
|
22
23
|
- Mount `mountMcpRoute` as one modern `POST` endpoint. It creates a fresh
|
|
23
24
|
protocol server per request, emits no session id, and refuses subscriptions.
|
|
24
25
|
- Keep tool catalogs private by default. Public caching requires an explicit
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
App-runtime MCP server scaffolding for s-m-r-t apps. Provides:
|
|
4
4
|
|
|
5
|
-
- **Core** — `createMcpAppServer({ smrtOptions, serverInfo, allowedClassNames, publicToolPatterns?, toolListCache?, toolPolicy?, workflowAssertions? })` returning `{ listTools, callTool }` wired to `@happyvertical/smrt-core/generators/mcp`.
|
|
5
|
+
- **Core** — `createMcpAppServer({ smrtOptions, serverInfo, allowedClassNames, publicToolPatterns?, toolListCache?, toolPolicy?, workflowAssertions?, workflowTools? })` returning `{ listTools, callTool }` wired to `@happyvertical/smrt-core/generators/mcp`.
|
|
6
6
|
- **SvelteKit adapters** (`./sveltekit`) — `mountMcpRoute` mounts a modern
|
|
7
7
|
2026-07-28 stateless Streamable HTTP MCP endpoint. The REST-shaped
|
|
8
8
|
`mountMcpToolsRoute` / `mountMcpCallRoute` aliases remain available for one
|
|
@@ -12,8 +12,11 @@ For piping a deployed app's MCP surface to a local stdio MCP client, see `@happy
|
|
|
12
12
|
|
|
13
13
|
For public deployments, follow the
|
|
14
14
|
[remote MCP authorization contract](../../docs/content/architecture/remote-mcp-authorization.md).
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
The server trusts the principal supplied by the application adapter. Validate
|
|
16
|
+
bearer tokens at an application gateway or with the server-only `./auth` JWT
|
|
17
|
+
adapter, then resolve fresh application membership and tenant authority on every
|
|
18
|
+
request. The OAuth issuer remains operator-owned; this package does not implement
|
|
19
|
+
an OAuth authorization server.
|
|
17
20
|
|
|
18
21
|
```ts
|
|
19
22
|
// src/lib/server/mcp.ts
|
|
@@ -44,6 +47,39 @@ export const mcpServer = createMcpAppServer({
|
|
|
44
47
|
});
|
|
45
48
|
```
|
|
46
49
|
|
|
50
|
+
`workflowTools` composes explicitly declared application workflows into this
|
|
51
|
+
same catalog. Every workflow must declare its canonical `effect`,
|
|
52
|
+
`idempotent`, and `openWorld` values; title, icons, and a portable UI resource
|
|
53
|
+
association are host presentation metadata only. The configured `toolPolicy`
|
|
54
|
+
and `workflowAssertions` run for both discovery and direct calls before the
|
|
55
|
+
workflow handler receives the trusted principal and arguments.
|
|
56
|
+
Authored names must be lowercase snake_case strings of at most 64 characters;
|
|
57
|
+
input and output schemas must both have object roots. Invalid declarations fail
|
|
58
|
+
at server construction. Ordinary handler exceptions return a generic `isError`
|
|
59
|
+
result with text and structured error data; internal exception details are not
|
|
60
|
+
exposed. Intentional `McpAccessError` denials retain the transport's access-error
|
|
61
|
+
contract.
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
workflowTools: [{
|
|
65
|
+
name: 'application_prepare',
|
|
66
|
+
description: 'Prepare an application for human review',
|
|
67
|
+
title: 'Prepare application',
|
|
68
|
+
inputSchema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
|
|
69
|
+
outputSchema: { type: 'object', properties: { prepared: { type: 'boolean' } } },
|
|
70
|
+
effect: 'write', idempotent: true, openWorld: false,
|
|
71
|
+
ui: { resourceUri: 'ui://application/prepare.html', visibility: ['app'] },
|
|
72
|
+
async execute({ arguments: args, principal }) {
|
|
73
|
+
return { content: [{ type: 'text', text: 'Prepared for review.' }], structuredContent: { prepared: true } };
|
|
74
|
+
},
|
|
75
|
+
}],
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The resulting descriptor preserves `_meta.ui.resourceUri` and optional
|
|
79
|
+
`_meta.ui.visibility` through SDK-v2 `tools/list`, while ordinary clients retain the text and structured result.
|
|
80
|
+
The resource implementation itself is staged separately; metadata neither
|
|
81
|
+
loads a resource nor grants an application permission.
|
|
82
|
+
|
|
47
83
|
```ts
|
|
48
84
|
// src/routes/api/mcp/+server.ts
|
|
49
85
|
import { mountMcpRoute } from '@happyvertical/smrt-app-mcp/sveltekit';
|
|
@@ -127,3 +163,77 @@ unauthenticated for both discovery and calls. If an older mount supplies only
|
|
|
127
163
|
`resolveAuthenticated: () => true` and no principal, discovery keeps its old
|
|
128
164
|
boolean behavior while calls remain user-less as before; migrate that mount to
|
|
129
165
|
`resolvePrincipal` for one identity across both routes.
|
|
166
|
+
|
|
167
|
+
## Prebuilt MCP Apps resources
|
|
168
|
+
|
|
169
|
+
Declare portable HTML resources alongside workflow tools. A URI must contain
|
|
170
|
+
its explicit version as a path segment; tool `ui.resourceUri` references must
|
|
171
|
+
resolve to a declared resource. The server snapshots UTF-8 bytes, a SHA-256
|
|
172
|
+
digest and portable MIME `text/html;profile=mcp-app` at construction time. It
|
|
173
|
+
never compiles source or inserts principal data into templates.
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
const server = createMcpAppServer({
|
|
177
|
+
smrtOptions: () => ({ db }),
|
|
178
|
+
serverInfo: { name: 'application', version: '1' },
|
|
179
|
+
allowedClassNames: [],
|
|
180
|
+
resources: [{
|
|
181
|
+
uri: 'ui://application/v1/view.html',
|
|
182
|
+
version: 'v1',
|
|
183
|
+
name: 'Application view',
|
|
184
|
+
html: prebuiltHtml,
|
|
185
|
+
}],
|
|
186
|
+
workflowTools: [viewWorkflow], // ui.resourceUri points at the declaration
|
|
187
|
+
resourcePolicy: ({ principal }) =>
|
|
188
|
+
principal?.id === ownerId && principal?.tenantId === activeTenantId,
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Resources are private by default and require a stable principal id plus an explicit
|
|
193
|
+
`resourcePolicy`; an omitted policy denies both listing and reads. Only an
|
|
194
|
+
explicit `public: true` declaration permits anonymous reads of a static
|
|
195
|
+
artifact. `resourcePolicy` runs afresh for both catalog and direct reads; errors
|
|
196
|
+
fail closed. Every associated tool must also pass the ordinary tool policy.
|
|
197
|
+
Unknown and denied URIs return the same error. The resource policy owns tenant
|
|
198
|
+
and owner restrictions; static templates contain no candidate records, tokens
|
|
199
|
+
or sessions. Retrieve changing data through authorized tools instead.
|
|
200
|
+
|
|
201
|
+
Optional `metadata` carries extension JSON into catalog/read `_meta`, with a
|
|
202
|
+
64 KiB and 16-level limit. Non-JSON values, accessors, cycles and reserved `ui`
|
|
203
|
+
or `com.happyvertical.smrt/resource` keys are rejected. Metadata grants no access.
|
|
204
|
+
|
|
205
|
+
The raw HTML budget is 100 KiB. CSP connection, resource, frame and base origins
|
|
206
|
+
default to empty lists; permissions default to none. Origins must be exact HTTPS
|
|
207
|
+
origins (WSS is additionally accepted for connections). Unknown CSP/permission
|
|
208
|
+
fields are rejected. Bundle assets inline or use literal absolute references to
|
|
209
|
+
explicitly declared resource/frame origins. Relative asset references,
|
|
210
|
+
CSS imports/escapes and alternate image/source functions, base/object/embed/meta tags, srcset and srcdoc are rejected
|
|
211
|
+
by the conservative static validation profile. HTML is parsed before validation;
|
|
212
|
+
script raw text and inert comments are not CSS. Decoded attribute URLs must still
|
|
213
|
+
match declared origins. Raster data images are permitted.
|
|
214
|
+
Dynamic JavaScript networking requires host CSP enforcement; declarations are
|
|
215
|
+
not a JavaScript sandbox. Host rendering and enforcement remain M4/M8 gates.
|
|
216
|
+
|
|
217
|
+
`mountMcpRoute` exposes native `resources/list` and `resources/read`, with
|
|
218
|
+
private, zero-TTL results and no subscriptions or HTTP sessions. Missing UI
|
|
219
|
+
capabilities do not remove ordinary text/structured tool results. The app CLI
|
|
220
|
+
bridge now defaults to this modern `/api/mcp` endpoint; use explicit
|
|
221
|
+
`transport: 'legacy-rest'` (or the generic bin's `--legacy-rest`) only during a
|
|
222
|
+
migration from `/api/mcp/tools` and `/api/mcp/call`. The modern bridge uses SDK v2,
|
|
223
|
+
re-resolves credentials per request, refuses redirects, and forwards resource
|
|
224
|
+
and tool metadata without inventing UI or extension capabilities.
|
|
225
|
+
|
|
226
|
+
Generated tool allow-lists use the generator-owned original class identity, even
|
|
227
|
+
when the advertised name is a canonical alias. Guards keyed by either the alias
|
|
228
|
+
or original tool name run before both direct and task dispatch. Authored workflows
|
|
229
|
+
retain their explicit names and effect policy; catalogs containing authored
|
|
230
|
+
workflows keep private cache scope because they have no generated tenant identity.
|
|
231
|
+
|
|
232
|
+
## Remote JWT authorization
|
|
233
|
+
|
|
234
|
+
The server-only `@happyvertical/smrt-app-mcp/auth` entry exports
|
|
235
|
+
`createMcpResourceAuth` for protected-resource metadata, bearer challenges and
|
|
236
|
+
JWT verification against a configured existing issuer. A required application
|
|
237
|
+
callback rechecks actor and active tenant membership per request. See the
|
|
238
|
+
[remote authorization contract](../../docs/content/architecture/remote-mcp-authorization.md)
|
|
239
|
+
for route wiring, local/hosted profiles and the explicit JWT revocation limits.
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** Server-only OAuth protected-resource boundary. This is not an issuer. */
|
|
2
|
+
import { type JWTPayload } from 'jose';
|
|
3
|
+
import type { McpAppPrincipal } from './server.js';
|
|
4
|
+
export type McpDeploymentProfile = 'local' | 'self-hosted' | 'cloud';
|
|
5
|
+
export interface McpVerifiedIdentity {
|
|
6
|
+
readonly issuer: string;
|
|
7
|
+
readonly subject: string;
|
|
8
|
+
readonly scopes: readonly string[];
|
|
9
|
+
readonly claims: Readonly<JWTPayload>;
|
|
10
|
+
}
|
|
11
|
+
export interface McpPrincipalMapping {
|
|
12
|
+
id: string;
|
|
13
|
+
tenantId?: string;
|
|
14
|
+
kind?: string;
|
|
15
|
+
roles?: string[];
|
|
16
|
+
}
|
|
17
|
+
export interface McpResourceAuthOptions {
|
|
18
|
+
profile: McpDeploymentProfile;
|
|
19
|
+
/** Exact public resource identifier; also the required token audience. */
|
|
20
|
+
resource: string;
|
|
21
|
+
/** Exact issuer identifier, including any trailing slash. */
|
|
22
|
+
issuer: string;
|
|
23
|
+
/** Operator-configured signing keys. Never derived from an untrusted token. */
|
|
24
|
+
jwksUri: string;
|
|
25
|
+
scopes: readonly string[];
|
|
26
|
+
algorithms: readonly ('RS256' | 'ES256')[];
|
|
27
|
+
/** Default at+jwt. Select JWT only for a provider that uses it for access tokens. */
|
|
28
|
+
tokenType?: 'at+jwt' | 'JWT';
|
|
29
|
+
/** Default true. Set false only for an explicitly single-tenant application. */
|
|
30
|
+
requireTenant?: boolean;
|
|
31
|
+
/** Re-run for every request; return null for revoked/disabled/unmapped users.
|
|
32
|
+
* Resolve active tenant/membership in trusted application state, never request
|
|
33
|
+
* arguments. Provider JWT revocation is NOT detected by offline validation.
|
|
34
|
+
*/
|
|
35
|
+
resolvePrincipal(identity: McpVerifiedIdentity): Promise<McpPrincipalMapping | null>;
|
|
36
|
+
}
|
|
37
|
+
export interface McpResourceAuth {
|
|
38
|
+
/** Mount GET at this URL. Paths use RFC 9728's well-known path insertion. */
|
|
39
|
+
readonly metadataUrl: string;
|
|
40
|
+
metadataResponse(): Response;
|
|
41
|
+
/** A failure response contains no token, claims or upstream error details. */
|
|
42
|
+
authenticate(request: Request): Promise<{
|
|
43
|
+
ok: true;
|
|
44
|
+
principal: McpAppPrincipal & {
|
|
45
|
+
id: string;
|
|
46
|
+
};
|
|
47
|
+
} | {
|
|
48
|
+
ok: false;
|
|
49
|
+
response: Response;
|
|
50
|
+
}>;
|
|
51
|
+
}
|
|
52
|
+
export declare function createMcpResourceAuth(options: McpResourceAuthOptions): McpResourceAuth;
|
|
53
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,OAAO,EAAsB,KAAK,UAAU,EAAa,MAAM,MAAM,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,aAAa,GAAG,OAAO,CAAC;AACrE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;CACvC;AACD,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AACD,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,oBAAoB,CAAC;IAC9B,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,UAAU,EAAE,SAAS,CAAC,OAAO,GAAG,OAAO,CAAC,EAAE,CAAC;IAC3C,qFAAqF;IACrF,SAAS,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAC;IAC7B,gFAAgF;IAChF,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,gBAAgB,CACd,QAAQ,EAAE,mBAAmB,GAC5B,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;CACxC;AAqCD,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,gBAAgB,IAAI,QAAQ,CAAC;IAC7B,8EAA8E;IAC9E,YAAY,CACV,OAAO,EAAE,OAAO,GACf,OAAO,CACN;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,SAAS,EAAE,eAAe,GAAG;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,GACzD;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,QAAQ,EAAE,QAAQ,CAAA;KAAE,CACpC,CAAC;CACH;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,sBAAsB,GAC9B,eAAe,CA0HjB"}
|