@happyvertical/smrt-app-mcp 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md ADDED
@@ -0,0 +1,22 @@
1
+ # @happyvertical/smrt-app-mcp
2
+
3
+ Application-scoped MCP server wrapper for SMRT apps.
4
+
5
+ ## Purpose
6
+
7
+ - Wraps `@happyvertical/smrt-core` MCP generation with app-level tool allow-lists.
8
+ - Filters unauthenticated tool visibility to public read-only tools.
9
+ - Provides workflow assertions so app servers can reject unsafe or incomplete tool calls before dispatch.
10
+
11
+ ## Patterns
12
+
13
+ - Generate tools from core, then filter by allowed class-name prefixes.
14
+ - Treat list/get tools as read-only; mutating tools require authentication unless the app deliberately wraps them with stronger policy.
15
+ - Keep app policy outside generated tool schemas. The wrapper owns auth, allow-list, and workflow assertion behavior.
16
+ - Return 404 for tools outside the app allow-list so private generated tools are not enumerated by mistake.
17
+
18
+ ## Gotchas
19
+
20
+ - Public tool patterns are still constrained by read-only detection.
21
+ - Workflow assertions run before generated tool dispatch and should be deterministic.
22
+ - This package is runtime app infrastructure, not the development MCP. Use `@happyvertical/smrt-dev-mcp` for agentic development knowledge, review, and architecture tooling.
package/CLAUDE.md ADDED
@@ -0,0 +1 @@
1
+ @AGENTS.md
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright <2025> <Happy Vertical Corporation>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # @happyvertical/smrt-app-mcp
2
+
3
+ App-runtime MCP server scaffolding for SMRT apps. Provides:
4
+
5
+ - **Core** — `createMcpAppServer({ smrtOptions, serverInfo, allowedClassNames, publicToolPatterns?, workflowAssertions? })` returning `{ listTools, callTool }` wired to `@happyvertical/smrt-core/generators/mcp`.
6
+ - **SvelteKit adapters** (`./sveltekit`) — `mountMcpToolsRoute` / `mountMcpCallRoute` for `/api/mcp/{tools,call}/+server.ts`.
7
+
8
+ For piping a deployed app's MCP surface to a local stdio MCP client, see `@happyvertical/smrt-app-cli` — the client-side runtime CLI exposes a `startMcpBridge()` default and a generic `smrt-mcp-bridge` bin.
9
+
10
+ ```ts
11
+ // src/lib/server/mcp.ts
12
+ import { createMcpAppServer, McpAccessError } from '@happyvertical/smrt-app-mcp';
13
+ import { adminResources } from '$lib/admin/resources';
14
+ import { getDbConfig } from './db';
15
+
16
+ export const mcpServer = createMcpAppServer({
17
+ smrtOptions: () => ({ db: getDbConfig() }),
18
+ serverInfo: { name: 'my-app', version: '0.1.0' },
19
+ allowedClassNames: adminResources.map((r) => r.className),
20
+ publicToolPatterns: () =>
21
+ (process.env.MY_APP_PUBLIC_MCP_TOOLS ?? '')
22
+ .split(',')
23
+ .map((s) => s.trim())
24
+ .filter(Boolean),
25
+ workflowAssertions: {
26
+ application_update: (args, user) => {
27
+ if (!user?.id) throw new McpAccessError(401, 'sign in first');
28
+ args.approvedByUserId = user.id;
29
+ },
30
+ },
31
+ });
32
+ ```
33
+
34
+ ```ts
35
+ // src/routes/api/mcp/tools/+server.ts
36
+ import { mountMcpToolsRoute } from '@happyvertical/smrt-app-mcp/sveltekit';
37
+ import { mcpServer } from '$lib/server/mcp';
38
+ export const GET = mountMcpToolsRoute(mcpServer);
39
+ ```
40
+
41
+ ```ts
42
+ // src/routes/api/mcp/call/+server.ts
43
+ import { mountMcpCallRoute } from '@happyvertical/smrt-app-mcp/sveltekit';
44
+ import { mcpServer } from '$lib/server/mcp';
45
+ export const POST = mountMcpCallRoute(mcpServer);
46
+ ```
47
+
@@ -0,0 +1,12 @@
1
+ class McpAccessError extends Error {
2
+ constructor(status, message) {
3
+ super(message);
4
+ this.status = status;
5
+ this.name = "McpAccessError";
6
+ }
7
+ status;
8
+ }
9
+ export {
10
+ McpAccessError as M
11
+ };
12
+ //# sourceMappingURL=errors-W_2Xu9nk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors-W_2Xu9nk.js","sources":["../../src/errors.ts"],"sourcesContent":["/**\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 ) {\n super(message);\n this.name = 'McpAccessError';\n }\n}\n"],"names":[],"mappings":"AAKO,MAAM,uBAAuB,MAAM;AAAA,EACxC,YACW,QACT,SACA;AACA,UAAM,OAAO;AAHJ,SAAA,SAAA;AAIT,SAAK,OAAO;AAAA,EACd;AAAA,EALW;AAMb;"}
@@ -0,0 +1,160 @@
1
+ /**
2
+ * `@happyvertical/smrt-app-mcp` — app-runtime MCP server scaffolding for
3
+ * SMRT apps. Exposes a SMRT app's MCP surface over HTTP, regardless of how
4
+ * the app is deployed.
5
+ *
6
+ * - **Core** (this module): `createMcpAppServer` wraps
7
+ * `@happyvertical/smrt-core/generators/mcp` with allow-listing, a public
8
+ * tool policy, and per-tool workflow assertions. Returns
9
+ * `{ listTools, callTool }` you can mount however you like.
10
+ *
11
+ * - **SvelteKit** (`@happyvertical/smrt-app-mcp/sveltekit`): thin route
12
+ * adapters that turn an `McpAppServer` into the GET/POST handlers a
13
+ * SvelteKit `+server.ts` expects. Additional transport adapters
14
+ * (standalone Node HTTP, serverless, Express/Hono/Fastify) can be added
15
+ * as sibling subpaths without changes to the core.
16
+ *
17
+ * For piping a deployed app's MCP surface to a local stdio MCP client
18
+ * (e.g. for editor/AI client integration), see
19
+ * `@happyvertical/smrt-app-cli` — the client-side counterpart owns the
20
+ * stdio bridge.
21
+ *
22
+ * @packageDocumentation
23
+ */
24
+
25
+ import { MCPConfig } from '@happyvertical/smrt-core/generators/mcp';
26
+ import { MCPResponse } from '@happyvertical/smrt-core/generators/mcp';
27
+ import { MCPTool } from '@happyvertical/smrt-core/generators/mcp';
28
+
29
+ /** Tool call inputs. */
30
+ export declare interface CallToolInput {
31
+ name: string;
32
+ arguments?: Record<string, unknown>;
33
+ /** Authenticated user; null/undefined means unauthenticated. */
34
+ user?: McpAppUser | null;
35
+ }
36
+
37
+ /**
38
+ * Lower-case `<class>_` prefixes the app considers "allowed core tools"
39
+ * given a list of SMRT class names. Used to build the allow-list for
40
+ * `McpAppServer.listTools`.
41
+ */
42
+ export declare function classNamePrefixes(classNames: readonly string[]): ReadonlySet<string>;
43
+
44
+ /**
45
+ * Build the app-runtime MCP server core. The returned object is intentionally
46
+ * framework-agnostic: HTTP wrappers live in `@happyvertical/smrt-app-mcp/sveltekit`
47
+ * and a stdio bridge lives in `@happyvertical/smrt-app-mcp/bin/smrt-mcp-bridge`.
48
+ */
49
+ export declare function createMcpAppServer(options: CreateMcpAppServerOptions): McpAppServer;
50
+
51
+ /**
52
+ * Options for `createMcpAppServer`.
53
+ */
54
+ export declare interface CreateMcpAppServerOptions {
55
+ /** SMRT context bag (db, etc.) passed to MCPGenerator per call. */
56
+ smrtOptions: McpSmrtOptionsThunk;
57
+ /** Server identity surfaced in the MCP protocol. */
58
+ serverInfo: Required<Pick<MCPConfig, 'name' | 'version'>> & Pick<MCPConfig, 'description'>;
59
+ /**
60
+ * SMRT class names the app wants to publish. Tools whose name does not
61
+ * start with any of these classes (lowercased + underscore) are filtered
62
+ * out, even if SMRT generated them.
63
+ */
64
+ allowedClassNames: readonly string[];
65
+ /**
66
+ * Optional thunk returning glob-ish patterns for read-only tools that
67
+ * unauthenticated callers are allowed to use. Defaults to an empty list
68
+ * (everything requires auth).
69
+ */
70
+ publicToolPatterns?: McpPublicToolPatternsThunk;
71
+ /**
72
+ * Optional per-tool guards. Keyed by tool name. The assertion runs after
73
+ * tool resolution and before `MCPGenerator.handleToolCall`; throwing
74
+ * `McpAccessError` aborts the call with the error's status. Implementations
75
+ * may mutate `args` to inject trusted fields.
76
+ */
77
+ workflowAssertions?: Record<string, McpWorkflowAssertion>;
78
+ }
79
+
80
+ /**
81
+ * Whether a given tool name starts with any of the configured class
82
+ * prefixes.
83
+ */
84
+ export declare function isAllowedCoreTool(toolName: string, prefixes: ReadonlySet<string>): boolean;
85
+
86
+ /**
87
+ * Check whether a tool name is currently allowed for unauthenticated callers
88
+ * given the configured public-tool patterns. Only read-only tools may ever
89
+ * be public, regardless of pattern.
90
+ */
91
+ export declare function isPublicToolName(toolName: string, patterns: readonly string[]): boolean;
92
+
93
+ /**
94
+ * Read-only tool detection. Generated SMRT MCP tools follow the naming
95
+ * convention `<class>_<verb>`; we treat `_list` and `_get` as read-only.
96
+ */
97
+ export declare function isReadOnlyToolName(toolName: string): boolean;
98
+
99
+ /** Tool listing inputs. */
100
+ export declare interface ListToolsInput {
101
+ /** Whether the calling principal is authenticated. */
102
+ authenticated: boolean;
103
+ }
104
+
105
+ /**
106
+ * Match a tool name against a glob-ish pattern with `*` wildcards.
107
+ *
108
+ * - Empty pattern → never matches.
109
+ * - `*` → matches everything.
110
+ * - `prefix_*` → matches anything starting with `prefix_`.
111
+ * - `*_suffix` → matches anything ending with `_suffix`.
112
+ * - `a_*_b` → matches any name containing `a_`, then any text, then `_b`.
113
+ *
114
+ * No regex characters are special besides `*` — the input is treated as a
115
+ * literal string with star wildcards.
116
+ */
117
+ export declare function matchesToolPattern(toolName: string, pattern: string): boolean;
118
+
119
+ /**
120
+ * Error returned by the MCP app server when a caller tries to use a tool
121
+ * they are not allowed to access. The HTTP layer should map `status` onto
122
+ * the response status code.
123
+ */
124
+ export declare class McpAccessError extends Error {
125
+ readonly status: number;
126
+ constructor(status: number, message: string);
127
+ }
128
+
129
+ /** Shape returned by `createMcpAppServer`. */
130
+ export declare interface McpAppServer {
131
+ listTools(input: ListToolsInput): Promise<MCPTool[]>;
132
+ callTool(input: CallToolInput): Promise<MCPResponse>;
133
+ /** Read-only view of the configured server identity. */
134
+ readonly serverInfo: CreateMcpAppServerOptions['serverInfo'];
135
+ }
136
+
137
+ /** Minimal user shape used for tool-call attribution. */
138
+ export declare interface McpAppUser {
139
+ id: string;
140
+ roles?: string[];
141
+ }
142
+
143
+ /** Public-tool patterns thunk — same lazy-evaluation rationale. */
144
+ export declare type McpPublicToolPatternsThunk = () => readonly string[];
145
+
146
+ /**
147
+ * SMRT options thunk — returns the `{ db }` (and similar) bag to pass into
148
+ * MCPGenerator's per-request context. A function is used so apps can lazily
149
+ * resolve env vars at call time.
150
+ */
151
+ export declare type McpSmrtOptionsThunk = () => Record<string, unknown>;
152
+
153
+ /**
154
+ * Workflow assertion hook signature. Throw `McpAccessError` to reject the
155
+ * call. Implementations may mutate `args` in place to inject server-trusted
156
+ * fields (e.g. clamping `approvedByUserId` to the authenticated user's id).
157
+ */
158
+ export declare type McpWorkflowAssertion = (args: Record<string, unknown>, user: McpAppUser | null) => void;
159
+
160
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,90 @@
1
+ import { M as McpAccessError } from "./chunks/errors-W_2Xu9nk.js";
2
+ import { MCPGenerator } from "@happyvertical/smrt-core/generators/mcp";
3
+ function matchesToolPattern(toolName, pattern) {
4
+ if (!pattern) return false;
5
+ if (pattern === "*") return true;
6
+ const parts = pattern.split("*");
7
+ if (parts.length === 1) return toolName === pattern;
8
+ let cursor = 0;
9
+ if (parts[0] && !toolName.startsWith(parts[0])) return false;
10
+ for (const part of parts) {
11
+ if (!part) continue;
12
+ const index = toolName.indexOf(part, cursor);
13
+ if (index < 0) return false;
14
+ cursor = index + part.length;
15
+ }
16
+ const last = parts.at(-1);
17
+ return !last || toolName.endsWith(last);
18
+ }
19
+ function isReadOnlyToolName(toolName) {
20
+ return toolName.endsWith("_list") || toolName.endsWith("_get");
21
+ }
22
+ function isPublicToolName(toolName, patterns) {
23
+ return isReadOnlyToolName(toolName) && patterns.some((pattern) => matchesToolPattern(toolName, pattern));
24
+ }
25
+ function classNamePrefixes(classNames) {
26
+ return new Set(classNames.map((className) => `${className.toLowerCase()}_`));
27
+ }
28
+ function isAllowedCoreTool(toolName, prefixes) {
29
+ for (const prefix of prefixes) {
30
+ if (toolName.startsWith(prefix)) return true;
31
+ }
32
+ return false;
33
+ }
34
+ function createMcpAppServer(options) {
35
+ const allowedPrefixes = classNamePrefixes(options.allowedClassNames);
36
+ const getPublicPatterns = options.publicToolPatterns ?? (() => []);
37
+ const workflowAssertions = options.workflowAssertions ?? {};
38
+ function makeGenerator(user) {
39
+ return new MCPGenerator(options.serverInfo, {
40
+ ...options.smrtOptions(),
41
+ user: user?.id ? { id: user.id, roles: user.roles } : void 0
42
+ });
43
+ }
44
+ async function listTools(input) {
45
+ const tools = await makeGenerator().generateTools();
46
+ const allowed = tools.filter(
47
+ (tool) => isAllowedCoreTool(tool.name, allowedPrefixes)
48
+ );
49
+ if (input.authenticated) return allowed;
50
+ const patterns = getPublicPatterns();
51
+ return allowed.filter((tool) => isPublicToolName(tool.name, patterns));
52
+ }
53
+ async function callTool(input) {
54
+ const args = input.arguments ?? {};
55
+ const user = input.user ?? null;
56
+ const tools = await listTools({ authenticated: true });
57
+ if (!tools.some((tool) => tool.name === input.name)) {
58
+ throw new McpAccessError(404, `Unknown MCP tool: ${input.name}`);
59
+ }
60
+ if (!user && !isPublicToolName(input.name, getPublicPatterns())) {
61
+ throw new McpAccessError(
62
+ 401,
63
+ `Authentication is required for MCP tool: ${input.name}`
64
+ );
65
+ }
66
+ const assertion = workflowAssertions[input.name];
67
+ if (assertion) {
68
+ assertion(args, user);
69
+ }
70
+ return makeGenerator(user).handleToolCall({
71
+ method: "tools/call",
72
+ params: { arguments: args, name: input.name }
73
+ });
74
+ }
75
+ return {
76
+ listTools,
77
+ callTool,
78
+ serverInfo: options.serverInfo
79
+ };
80
+ }
81
+ export {
82
+ McpAccessError,
83
+ classNamePrefixes,
84
+ createMcpAppServer,
85
+ isAllowedCoreTool,
86
+ isPublicToolName,
87
+ isReadOnlyToolName,
88
+ matchesToolPattern
89
+ };
90
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/tools.ts","../src/server.ts"],"sourcesContent":["/**\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 * `createMcpAppServer` returns the framework-agnostic core that backs an\n * app's HTTP MCP endpoints and stdio bridge. It wraps `MCPGenerator` from\n * `@happyvertical/smrt-core` with:\n *\n * - an allow-list of SMRT class names (so apps publish a subset of their\n * objects, not everything decorated with `@smrt()`),\n * - a public-tool policy for unauthenticated callers (read-only patterns\n * via `publicToolPatterns`),\n * - a pluggable `workflowAssertions` hook so apps can guard their own\n * domain-specific tool calls (e.g. \"approval requires an authenticated\n * user\") without that policy living in this package.\n *\n * @packageDocumentation\n */\n\nimport type {\n MCPConfig,\n MCPResponse,\n MCPTool,\n} from '@happyvertical/smrt-core/generators/mcp';\nimport { MCPGenerator } from '@happyvertical/smrt-core/generators/mcp';\nimport { McpAccessError } from './errors.js';\nimport {\n classNamePrefixes,\n isAllowedCoreTool,\n isPublicToolName,\n} from './tools.js';\n\n/** Minimal user shape used for tool-call attribution. */\nexport interface McpAppUser {\n id: string;\n roles?: string[];\n}\n\n/**\n * Workflow assertion hook signature. Throw `McpAccessError` to reject the\n * call. Implementations may mutate `args` in place to inject server-trusted\n * fields (e.g. clamping `approvedByUserId` to the authenticated user's id).\n */\nexport type McpWorkflowAssertion = (\n args: Record<string, unknown>,\n user: McpAppUser | null,\n) => void;\n\n/**\n * SMRT options thunk — returns the `{ db }` (and similar) bag to pass into\n * MCPGenerator's per-request context. A function is used so apps can lazily\n * resolve env vars at call time.\n */\nexport type McpSmrtOptionsThunk = () => Record<string, unknown>;\n\n/** Public-tool patterns thunk — same lazy-evaluation rationale. */\nexport type McpPublicToolPatternsThunk = () => readonly string[];\n\n/**\n * Options for `createMcpAppServer`.\n */\nexport interface CreateMcpAppServerOptions {\n /** SMRT context bag (db, etc.) passed to MCPGenerator per call. */\n smrtOptions: McpSmrtOptionsThunk;\n /** Server identity surfaced in the MCP protocol. */\n serverInfo: Required<Pick<MCPConfig, 'name' | 'version'>> &\n Pick<MCPConfig, 'description'>;\n /**\n * SMRT class names the app wants to publish. Tools whose name does not\n * start with any of these classes (lowercased + underscore) are filtered\n * out, even if SMRT generated them.\n */\n allowedClassNames: readonly string[];\n /**\n * Optional thunk returning glob-ish patterns for read-only tools that\n * unauthenticated callers are allowed to use. Defaults to an empty list\n * (everything requires auth).\n */\n publicToolPatterns?: McpPublicToolPatternsThunk;\n /**\n * Optional per-tool guards. Keyed by tool name. The assertion runs after\n * tool resolution and before `MCPGenerator.handleToolCall`; throwing\n * `McpAccessError` aborts the call with the error's status. Implementations\n * may mutate `args` to inject trusted fields.\n */\n workflowAssertions?: Record<string, McpWorkflowAssertion>;\n}\n\n/** Tool listing inputs. */\nexport interface ListToolsInput {\n /** Whether the calling principal is authenticated. */\n authenticated: boolean;\n}\n\n/** Tool call inputs. */\nexport interface CallToolInput {\n name: string;\n arguments?: Record<string, unknown>;\n /** Authenticated user; null/undefined means unauthenticated. */\n user?: McpAppUser | null;\n}\n\n/** Shape returned by `createMcpAppServer`. */\nexport interface McpAppServer {\n listTools(input: ListToolsInput): Promise<MCPTool[]>;\n callTool(input: CallToolInput): Promise<MCPResponse>;\n /** Read-only view of the configured server identity. */\n readonly serverInfo: CreateMcpAppServerOptions['serverInfo'];\n}\n\n/**\n * Build the app-runtime MCP server core. The returned object is intentionally\n * framework-agnostic: HTTP wrappers live in `@happyvertical/smrt-app-mcp/sveltekit`\n * and a stdio bridge lives in `@happyvertical/smrt-app-mcp/bin/smrt-mcp-bridge`.\n */\nexport function createMcpAppServer(\n options: CreateMcpAppServerOptions,\n): McpAppServer {\n const allowedPrefixes = classNamePrefixes(options.allowedClassNames);\n const getPublicPatterns =\n options.publicToolPatterns ?? ((): readonly string[] => []);\n const workflowAssertions = options.workflowAssertions ?? {};\n\n function makeGenerator(user?: McpAppUser | null): MCPGenerator {\n return new MCPGenerator(options.serverInfo as MCPConfig, {\n ...options.smrtOptions(),\n user: user?.id ? { id: user.id, roles: user.roles } : undefined,\n });\n }\n\n async function listTools(input: ListToolsInput): Promise<MCPTool[]> {\n const tools = await makeGenerator().generateTools();\n const allowed = tools.filter((tool) =>\n isAllowedCoreTool(tool.name, allowedPrefixes),\n );\n if (input.authenticated) return allowed;\n const patterns = getPublicPatterns();\n return allowed.filter((tool) => isPublicToolName(tool.name, patterns));\n }\n\n async function callTool(input: CallToolInput): Promise<MCPResponse> {\n const args = input.arguments ?? {};\n const user = input.user ?? null;\n const tools = await listTools({ authenticated: true });\n if (!tools.some((tool) => tool.name === input.name)) {\n throw new McpAccessError(404, `Unknown MCP tool: ${input.name}`);\n }\n\n if (!user && !isPublicToolName(input.name, getPublicPatterns())) {\n throw new McpAccessError(\n 401,\n `Authentication is required for MCP tool: ${input.name}`,\n );\n }\n\n const assertion = workflowAssertions[input.name];\n if (assertion) {\n assertion(args, user);\n }\n\n return makeGenerator(user).handleToolCall({\n method: 'tools/call',\n params: { arguments: args, name: input.name },\n });\n }\n\n return {\n listTools,\n callTool,\n serverInfo: options.serverInfo,\n };\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": "1.0.0",
3
+ "timestamp": 1782177067289,
4
+ "packageName": "@happyvertical/smrt-app-mcp",
5
+ "packageVersion": "0.30.0",
6
+ "objects": {},
7
+ "moduleType": "smrt",
8
+ "smrtDependencies": [
9
+ "@happyvertical/smrt-core"
10
+ ]
11
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "generatedAt": "2026-06-23T01:11:08.388Z",
4
+ "packageName": "@happyvertical/smrt-app-mcp",
5
+ "packageVersion": "0.30.0",
6
+ "sourceManifestPath": "dist/manifest.json",
7
+ "agentDocPath": "AGENTS.md",
8
+ "sourceHashes": {
9
+ "manifest": "4a8ec60d5f8af35f27c15d4ef0713d0931026d05f93cd7b6a29b8c5c9f563bc0",
10
+ "packageJson": "8324f7bf966036f890661be6ebebabd0d4fec45afca13d494eb0e010a75e61f1",
11
+ "agents": "fd1dc36d1530f81aae49e6efa2e2f5011a8a62d30816f25649d4cb597fc5662a"
12
+ },
13
+ "exports": [
14
+ ".",
15
+ "./sveltekit"
16
+ ],
17
+ "dependencies": {
18
+ "@happyvertical/smrt-core": "workspace:*",
19
+ "@modelcontextprotocol/sdk": "^1.25.2",
20
+ "@types/node": "25.0.9",
21
+ "typescript": "^5.9.3",
22
+ "vite": "^7.3.1",
23
+ "vitest": "^4.0.17"
24
+ },
25
+ "smrtDependencies": [
26
+ "@happyvertical/smrt-core"
27
+ ],
28
+ "sdkDependencies": [],
29
+ "tags": [],
30
+ "risks": [],
31
+ "objects": [],
32
+ "surfaces": [],
33
+ "prompts": [],
34
+ "relationshipsV2": {
35
+ "foreignKeyFields": 0,
36
+ "crossPackageRefFields": 0,
37
+ "junctionCollections": 0,
38
+ "hierarchicalObjects": 0,
39
+ "polymorphicAssociations": 0,
40
+ "uuidColumns": 0
41
+ },
42
+ "agentDoc": "# @happyvertical/smrt-app-mcp\n\nApplication-scoped MCP server wrapper for SMRT apps.\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- Provides workflow assertions so app servers can reject unsafe or incomplete tool calls before dispatch.\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\n## Gotchas\n\n- Public tool patterns are still constrained by read-only detection.\n- Workflow assertions run before generated tool dispatch and should be deterministic.\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"
43
+ }
@@ -0,0 +1,127 @@
1
+ import { MCPConfig } from '@happyvertical/smrt-core/generators/mcp';
2
+ import { MCPResponse } from '@happyvertical/smrt-core/generators/mcp';
3
+ import { MCPTool } from '@happyvertical/smrt-core/generators/mcp';
4
+
5
+ /** Tool call inputs. */
6
+ declare interface CallToolInput {
7
+ name: string;
8
+ arguments?: Record<string, unknown>;
9
+ /** Authenticated user; null/undefined means unauthenticated. */
10
+ user?: McpAppUser | null;
11
+ }
12
+
13
+ /**
14
+ * Options for `createMcpAppServer`.
15
+ */
16
+ declare interface CreateMcpAppServerOptions {
17
+ /** SMRT context bag (db, etc.) passed to MCPGenerator per call. */
18
+ smrtOptions: McpSmrtOptionsThunk;
19
+ /** Server identity surfaced in the MCP protocol. */
20
+ serverInfo: Required<Pick<MCPConfig, 'name' | 'version'>> & Pick<MCPConfig, 'description'>;
21
+ /**
22
+ * SMRT class names the app wants to publish. Tools whose name does not
23
+ * start with any of these classes (lowercased + underscore) are filtered
24
+ * out, even if SMRT generated them.
25
+ */
26
+ allowedClassNames: readonly string[];
27
+ /**
28
+ * Optional thunk returning glob-ish patterns for read-only tools that
29
+ * unauthenticated callers are allowed to use. Defaults to an empty list
30
+ * (everything requires auth).
31
+ */
32
+ publicToolPatterns?: McpPublicToolPatternsThunk;
33
+ /**
34
+ * Optional per-tool guards. Keyed by tool name. The assertion runs after
35
+ * tool resolution and before `MCPGenerator.handleToolCall`; throwing
36
+ * `McpAccessError` aborts the call with the error's status. Implementations
37
+ * may mutate `args` to inject trusted fields.
38
+ */
39
+ workflowAssertions?: Record<string, McpWorkflowAssertion>;
40
+ }
41
+
42
+ /** Tool listing inputs. */
43
+ declare interface ListToolsInput {
44
+ /** Whether the calling principal is authenticated. */
45
+ authenticated: boolean;
46
+ }
47
+
48
+ /**
49
+ * Error returned by the MCP app server when a caller tries to use a tool
50
+ * they are not allowed to access. The HTTP layer should map `status` onto
51
+ * the response status code.
52
+ */
53
+ export declare class McpAccessError extends Error {
54
+ readonly status: number;
55
+ constructor(status: number, message: string);
56
+ }
57
+
58
+ /** Shape returned by `createMcpAppServer`. */
59
+ export declare interface McpAppServer {
60
+ listTools(input: ListToolsInput): Promise<MCPTool[]>;
61
+ callTool(input: CallToolInput): Promise<MCPResponse>;
62
+ /** Read-only view of the configured server identity. */
63
+ readonly serverInfo: CreateMcpAppServerOptions['serverInfo'];
64
+ }
65
+
66
+ /** Minimal user shape used for tool-call attribution. */
67
+ declare interface McpAppUser {
68
+ id: string;
69
+ roles?: string[];
70
+ }
71
+
72
+ /** Public-tool patterns thunk — same lazy-evaluation rationale. */
73
+ declare type McpPublicToolPatternsThunk = () => readonly string[];
74
+
75
+ /**
76
+ * SMRT options thunk — returns the `{ db }` (and similar) bag to pass into
77
+ * MCPGenerator's per-request context. A function is used so apps can lazily
78
+ * resolve env vars at call time.
79
+ */
80
+ declare type McpSmrtOptionsThunk = () => Record<string, unknown>;
81
+
82
+ /** Locals reader used to pull the authenticated user out of `event.locals`. */
83
+ export declare type McpUserResolver = (event: SvelteKitRequestEvent) => McpAppUser | null | undefined;
84
+
85
+ /**
86
+ * Workflow assertion hook signature. Throw `McpAccessError` to reject the
87
+ * call. Implementations may mutate `args` in place to inject server-trusted
88
+ * fields (e.g. clamping `approvedByUserId` to the authenticated user's id).
89
+ */
90
+ declare type McpWorkflowAssertion = (args: Record<string, unknown>, user: McpAppUser | null) => void;
91
+
92
+ /**
93
+ * Mount `server.callTool` as a `POST` handler that expects
94
+ * `{ name, arguments }` in the JSON body.
95
+ */
96
+ export declare function mountMcpCallRoute(server: McpAppServer, options?: MountMcpRouteOptions): SvelteKitHandler;
97
+
98
+ /** Options shared by both route mounts. */
99
+ export declare interface MountMcpRouteOptions {
100
+ /**
101
+ * Resolve the authenticated user from `event.locals`. Defaults to
102
+ * `event.locals.user`.
103
+ */
104
+ resolveUser?: McpUserResolver;
105
+ /**
106
+ * Resolve whether the request is authenticated. Defaults to
107
+ * `Boolean(event.locals.user)`.
108
+ */
109
+ resolveAuthenticated?: (event: SvelteKitRequestEvent) => boolean;
110
+ }
111
+
112
+ /**
113
+ * Mount `server.listTools` as a `GET` handler. Returns the tool list shape
114
+ * `{ tools }` for compatibility with the stock MCP bridge.
115
+ */
116
+ export declare function mountMcpToolsRoute(server: McpAppServer, options?: MountMcpRouteOptions): SvelteKitHandler;
117
+
118
+ declare type SvelteKitHandler = (event: SvelteKitRequestEvent) => Promise<Response>;
119
+
120
+ /** Minimal subset of a SvelteKit RequestEvent we actually touch. */
121
+ declare type SvelteKitRequestEvent = {
122
+ locals?: Record<string, unknown>;
123
+ request: Request;
124
+ url: URL;
125
+ };
126
+
127
+ export { }
@@ -0,0 +1,53 @@
1
+ import { M as McpAccessError } from "./chunks/errors-W_2Xu9nk.js";
2
+ const defaultResolveUser = (event) => event.locals?.user ?? null;
3
+ const defaultResolveAuthenticated = (event) => Boolean(event.locals?.user);
4
+ function mountMcpToolsRoute(server, options = {}) {
5
+ const resolveAuthenticated = options.resolveAuthenticated ?? defaultResolveAuthenticated;
6
+ return async (event) => {
7
+ try {
8
+ const tools = await server.listTools({
9
+ authenticated: resolveAuthenticated(event)
10
+ });
11
+ return jsonResponse({ tools });
12
+ } catch (error) {
13
+ if (error instanceof McpAccessError) {
14
+ return jsonResponse({ error: error.message }, error.status);
15
+ }
16
+ throw error;
17
+ }
18
+ };
19
+ }
20
+ function mountMcpCallRoute(server, options = {}) {
21
+ const resolveUser = options.resolveUser ?? defaultResolveUser;
22
+ return async (event) => {
23
+ const body = await event.request.json().catch(() => null);
24
+ if (!body?.name) {
25
+ return jsonResponse({ error: "name is required." }, 400);
26
+ }
27
+ const input = {
28
+ arguments: body.arguments ?? {},
29
+ name: body.name,
30
+ user: resolveUser(event) ?? null
31
+ };
32
+ try {
33
+ return jsonResponse(await server.callTool(input));
34
+ } catch (error) {
35
+ if (error instanceof McpAccessError) {
36
+ return jsonResponse({ error: error.message }, error.status);
37
+ }
38
+ throw error;
39
+ }
40
+ };
41
+ }
42
+ function jsonResponse(body, status = 200) {
43
+ return new Response(JSON.stringify(body), {
44
+ status,
45
+ headers: { "content-type": "application/json" }
46
+ });
47
+ }
48
+ export {
49
+ McpAccessError,
50
+ mountMcpCallRoute,
51
+ mountMcpToolsRoute
52
+ };
53
+ //# sourceMappingURL=sveltekit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sveltekit.js","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/tools/+server.ts\n * import { mountMcpToolsRoute } from '@happyvertical/smrt-app-mcp/sveltekit';\n * import { mcpServer } from '$lib/server/mcp';\n * export const GET = mountMcpToolsRoute(mcpServer);\n * ```\n */\n\nimport { McpAccessError } from './errors.js';\nimport type { CallToolInput, McpAppServer, McpAppUser } 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\ntype SvelteKitHandler = (event: SvelteKitRequestEvent) => Promise<Response>;\n\n/** Locals reader used to pull the authenticated user out of `event.locals`. */\nexport type McpUserResolver = (\n event: SvelteKitRequestEvent,\n) => McpAppUser | null | undefined;\n\nconst defaultResolveUser: McpUserResolver = (event) =>\n (event.locals?.user ?? null) as McpAppUser | null;\n\nconst defaultResolveAuthenticated = (event: SvelteKitRequestEvent): boolean =>\n Boolean(event.locals?.user);\n\n/** Options shared by both route mounts. */\nexport interface MountMcpRouteOptions {\n /**\n * Resolve the authenticated user from `event.locals`. Defaults to\n * `event.locals.user`.\n */\n resolveUser?: McpUserResolver;\n /**\n * Resolve whether the request is authenticated. Defaults to\n * `Boolean(event.locals.user)`.\n */\n resolveAuthenticated?: (event: SvelteKitRequestEvent) => boolean;\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 */\nexport function mountMcpToolsRoute(\n server: McpAppServer,\n options: MountMcpRouteOptions = {},\n): SvelteKitHandler {\n const resolveAuthenticated =\n options.resolveAuthenticated ?? defaultResolveAuthenticated;\n\n return async (event) => {\n try {\n const tools = await server.listTools({\n authenticated: resolveAuthenticated(event),\n });\n return jsonResponse({ tools });\n } catch (error) {\n if (error instanceof McpAccessError) {\n return jsonResponse({ error: error.message }, 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 */\nexport function mountMcpCallRoute(\n server: McpAppServer,\n options: MountMcpRouteOptions = {},\n): SvelteKitHandler {\n const resolveUser = options.resolveUser ?? defaultResolveUser;\n\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 user: resolveUser(event) ?? null,\n };\n\n try {\n return jsonResponse(await server.callTool(input));\n } catch (error) {\n if (error instanceof McpAccessError) {\n return jsonResponse({ error: error.message }, error.status);\n }\n throw error;\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 { McpAppServer } from './server.js';\n"],"names":[],"mappings":";AAiCA,MAAM,qBAAsC,CAAC,UAC1C,MAAM,QAAQ,QAAQ;AAEzB,MAAM,8BAA8B,CAAC,UACnC,QAAQ,MAAM,QAAQ,IAAI;AAoBrB,SAAS,mBACd,QACA,UAAgC,IACd;AAClB,QAAM,uBACJ,QAAQ,wBAAwB;AAElC,SAAO,OAAO,UAAU;AACtB,QAAI;AACF,YAAM,QAAQ,MAAM,OAAO,UAAU;AAAA,QACnC,eAAe,qBAAqB,KAAK;AAAA,MAAA,CAC1C;AACD,aAAO,aAAa,EAAE,OAAO;AAAA,IAC/B,SAAS,OAAO;AACd,UAAI,iBAAiB,gBAAgB;AACnC,eAAO,aAAa,EAAE,OAAO,MAAM,QAAA,GAAW,MAAM,MAAM;AAAA,MAC5D;AACA,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAMO,SAAS,kBACd,QACA,UAAgC,IACd;AAClB,QAAM,cAAc,QAAQ,eAAe;AAE3C,SAAO,OAAO,UAAU;AACtB,UAAM,OAAQ,MAAM,MAAM,QAAQ,OAAO,MAAM,MAAM,IAAI;AAKzD,QAAI,CAAC,MAAM,MAAM;AACf,aAAO,aAAa,EAAE,OAAO,oBAAA,GAAuB,GAAG;AAAA,IACzD;AAEA,UAAM,QAAuB;AAAA,MAC3B,WAAW,KAAK,aAAa,CAAA;AAAA,MAC7B,MAAM,KAAK;AAAA,MACX,MAAM,YAAY,KAAK,KAAK;AAAA,IAAA;AAG9B,QAAI;AACF,aAAO,aAAa,MAAM,OAAO,SAAS,KAAK,CAAC;AAAA,IAClD,SAAS,OAAO;AACd,UAAI,iBAAiB,gBAAgB;AACnC,eAAO,aAAa,EAAE,OAAO,MAAM,QAAA,GAAW,MAAM,MAAM;AAAA,MAC5D;AACA,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAEA,SAAS,aAAa,MAAe,SAAS,KAAe;AAC3D,SAAO,IAAI,SAAS,KAAK,UAAU,IAAI,GAAG;AAAA,IACxC;AAAA,IACA,SAAS,EAAE,gBAAgB,mBAAA;AAAA,EAAmB,CAC/C;AACH;"}
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@happyvertical/smrt-app-mcp",
3
+ "version": "0.30.0",
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
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "./sveltekit": {
14
+ "types": "./dist/sveltekit.d.ts",
15
+ "import": "./dist/sveltekit.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "CLAUDE.md",
20
+ "README.md",
21
+ "dist",
22
+ "AGENTS.md"
23
+ ],
24
+ "publishConfig": {
25
+ "registry": "https://registry.npmjs.org",
26
+ "access": "public"
27
+ },
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/happyvertical/smrt.git",
31
+ "directory": "packages/smrt-app-mcp"
32
+ },
33
+ "bugs": {
34
+ "url": "https://github.com/happyvertical/smrt/issues"
35
+ },
36
+ "homepage": "https://github.com/happyvertical/smrt/tree/main/packages/smrt-app-mcp#readme",
37
+ "license": "MIT",
38
+ "keywords": [
39
+ "smrt",
40
+ "mcp",
41
+ "model-context-protocol",
42
+ "sveltekit"
43
+ ],
44
+ "author": "HappyVertical",
45
+ "dependencies": {
46
+ "@modelcontextprotocol/sdk": "^1.25.2",
47
+ "@happyvertical/smrt-core": "0.30.0"
48
+ },
49
+ "devDependencies": {
50
+ "@types/node": "25.0.9",
51
+ "typescript": "^5.9.3",
52
+ "vite": "^7.3.1",
53
+ "vitest": "^4.0.17"
54
+ },
55
+ "scripts": {
56
+ "build": "vite build --mode library",
57
+ "build:watch": "vite build --mode library --watch",
58
+ "clean": "rm -rf dist",
59
+ "dev": "npm run build:watch",
60
+ "check": "tsc --noEmit",
61
+ "test": "vitest run",
62
+ "test:watch": "vitest",
63
+ "typecheck": "tsc --noEmit",
64
+ "verify:pack": "node ../../scripts/verify-package-types-exports.js ."
65
+ }
66
+ }