@happyvertical/smrt-app-mcp 0.40.53 → 0.40.55

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/dist/index.d.ts CHANGED
@@ -25,6 +25,8 @@
25
25
  import { MCPConfig } from '@happyvertical/smrt-core/generators/mcp';
26
26
  import { MCPResponse } from '@happyvertical/smrt-core/generators/mcp';
27
27
  import { MCPTool } from '@happyvertical/smrt-core/generators/mcp';
28
+ import { Server } from '@modelcontextprotocol/server';
29
+ import { ServerContext } from '@modelcontextprotocol/server';
28
30
 
29
31
  /** Tool call inputs. */
30
32
  export declare interface CallToolInput {
@@ -88,6 +90,15 @@ export declare interface CreateMcpAppServerOptions {
88
90
  workflowAssertions?: Record<string, McpWorkflowAssertion>;
89
91
  }
90
92
 
93
+ /**
94
+ * Adapt an app MCP core to the SDK v2 low-level server protocol.
95
+ *
96
+ * Transport ownership remains with the caller. In particular, this does not
97
+ * add a production HTTP endpoint; it is safe to compose with `serveStdio` or
98
+ * `createMcpHandler` in a deployment that supplies its own authentication.
99
+ */
100
+ export declare function createMcpProtocolServer(appServer: McpAppServer, options?: McpProtocolServerOptions): Server;
101
+
91
102
  /**
92
103
  * Whether a given tool name starts with any of the configured class
93
104
  * prefixes.
@@ -184,6 +195,11 @@ export declare interface McpAppUser extends McpAppPrincipal {
184
195
  id: string;
185
196
  }
186
197
 
198
+ export declare interface McpProtocolServerOptions {
199
+ /** Resolve the authenticated application principal for each MCP request. */
200
+ principal?: McpAppPrincipal | null | ((context: ServerContext) => McpAppPrincipal | null | Promise<McpAppPrincipal | null>);
201
+ }
202
+
187
203
  /** Public-tool patterns thunk — same lazy-evaluation rationale. */
188
204
  export declare type McpPublicToolPatternsThunk = () => readonly string[];
189
205
 
package/dist/index.js CHANGED
@@ -1,5 +1,35 @@
1
1
  import { n as McpAccessError, t as MCP_TOOL_ACCESS_DENIED_CODE } from "./chunks/errors-CHYu0Vr2.js";
2
+ import { ProtocolError, ProtocolErrorCode, Server } from "@modelcontextprotocol/server";
2
3
  import { MCPGenerator } from "@happyvertical/smrt-core/generators/mcp";
4
+ //#region src/protocol.ts
5
+ async function resolvePrincipal(option, context) {
6
+ if (typeof option === "function") return await option(context) ?? null;
7
+ return option ?? null;
8
+ }
9
+ function createMcpProtocolServer(appServer, options = {}) {
10
+ const server = new Server(appServer.serverInfo, { capabilities: { tools: {} } });
11
+ server.setRequestHandler("tools/list", async (_request, context) => ({ tools: await appServer.listTools({ principal: await resolvePrincipal(options.principal, context) }) }));
12
+ server.setRequestHandler("tools/call", async (request, context) => {
13
+ try {
14
+ return await appServer.callTool({
15
+ name: request.params.name,
16
+ arguments: request.params.arguments,
17
+ principal: await resolvePrincipal(options.principal, context)
18
+ });
19
+ } catch (error) {
20
+ if (error instanceof McpAccessError) {
21
+ const { code, retryable } = error.metadata;
22
+ throw new ProtocolError(error.status === 404 ? ProtocolErrorCode.InvalidParams : ProtocolErrorCode.InvalidRequest, error.message, {
23
+ ...typeof code === "string" ? { code } : {},
24
+ ...typeof retryable === "boolean" ? { retryable } : {}
25
+ });
26
+ }
27
+ throw error;
28
+ }
29
+ });
30
+ return server;
31
+ }
32
+ //#endregion
3
33
  //#region src/tools.ts
4
34
  function matchesToolPattern(toolName, pattern) {
5
35
  if (!pattern) return false;
@@ -114,6 +144,6 @@ function createMcpAppServer(options) {
114
144
  };
115
145
  }
116
146
  //#endregion
117
- export { MCP_TOOL_ACCESS_DENIED_CODE, McpAccessError, classNamePrefixes, createMcpAppServer, isAllowedCoreTool, isPublicToolName, isReadOnlyToolName, matchesToolPattern };
147
+ export { MCP_TOOL_ACCESS_DENIED_CODE, McpAccessError, classNamePrefixes, createMcpAppServer, createMcpProtocolServer, isAllowedCoreTool, isPublicToolName, isReadOnlyToolName, matchesToolPattern };
118
148
 
119
149
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"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 * - an optional principal-aware `toolPolicy`, used for both discovery and\n * direct calls,\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 { MCP_TOOL_ACCESS_DENIED_CODE, McpAccessError } from './errors.js';\nimport {\n classNamePrefixes,\n isAllowedCoreTool,\n isPublicToolName,\n} from './tools.js';\n\n/**\n * Generic authenticated caller information available to app-MCP policy.\n *\n * `kind`, `roles`, and `scopes` are deliberately unconstrained so an app can\n * represent a human or a scoped service without this package encoding an\n * application's identity or capability model. A missing principal means the\n * request is unauthenticated.\n */\nexport interface McpAppPrincipal {\n id?: string;\n kind?: string;\n roles?: string[];\n scopes?: string[];\n}\n\n/** Minimal legacy user shape used for generated tool-call attribution. */\nexport interface McpAppUser extends McpAppPrincipal {\n id: string;\n}\n\n/** Context supplied to the optional per-tool principal policy. */\nexport interface McpToolPolicyContext {\n principal: McpAppPrincipal | null;\n tool: MCPTool;\n}\n\n/**\n * Per-tool access policy. Return `true` to expose/allow the tool and `false`\n * to hide it from discovery and deny a direct call. A thrown error is treated\n * as a denial so policy implementation details cannot escape the app-MCP\n * boundary.\n */\nexport type McpToolPolicy = (\n context: McpToolPolicyContext,\n) => boolean | Promise<boolean>;\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 generic principal-aware tool policy. It is evaluated for every\n * tool that passes the app allow-list and base public/authenticated policy,\n * for both discovery and a direct call.\n */\n toolPolicy?: McpToolPolicy;\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 /** Caller used for public/authenticated and optional tool-policy checks. */\n principal?: McpAppPrincipal | null;\n /**\n * Backwards-compatible authenticated marker. New mounts should pass\n * `principal` so discovery and direct calls use the same identity.\n */\n authenticated?: boolean;\n}\n\n/** Tool call inputs. */\nexport interface CallToolInput {\n name: string;\n arguments?: Record<string, unknown>;\n /** Caller used for public/authenticated and optional tool-policy checks. */\n principal?: McpAppPrincipal | null;\n /**\n * Backwards-compatible user input. New callers should pass `principal`.\n * Null/undefined means unauthenticated.\n */\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 toolPolicy = options.toolPolicy;\n const workflowAssertions = options.workflowAssertions ?? {};\n\n function userForGenerator(\n principal?: McpAppPrincipal | null,\n ): McpAppUser | undefined {\n if (!principal?.id) return undefined;\n return { id: principal.id, roles: principal.roles };\n }\n\n function makeGenerator(principal?: McpAppPrincipal | null): MCPGenerator {\n const user = userForGenerator(principal);\n return new MCPGenerator(options.serverInfo as MCPConfig, {\n ...options.smrtOptions(),\n user,\n });\n }\n\n function principalForList(input: ListToolsInput): McpAppPrincipal | null {\n if (input.principal !== undefined) return input.principal;\n // Preserve callers of the original boolean API without inventing an id.\n return input.authenticated ? {} : null;\n }\n\n function principalForCall(input: CallToolInput): McpAppPrincipal | null {\n if (input.principal !== undefined) return input.principal;\n return input.user ?? null;\n }\n\n async function allowedTools(): Promise<MCPTool[]> {\n const tools = await makeGenerator().generateTools();\n return tools.filter((tool) =>\n isAllowedCoreTool(tool.name, allowedPrefixes),\n );\n }\n\n function passesBasePolicy(\n tool: MCPTool,\n principal: McpAppPrincipal | null,\n publicPatterns?: readonly string[],\n ): boolean {\n if (principal) return true;\n return isPublicToolName(tool.name, publicPatterns ?? []);\n }\n\n async function passesToolPolicy(\n tool: MCPTool,\n principal: McpAppPrincipal | null,\n ): Promise<boolean> {\n if (!toolPolicy) return true;\n try {\n return Boolean(await toolPolicy({ principal, tool }));\n } catch {\n // A policy failure must fail closed and never leak implementation detail.\n return false;\n }\n }\n\n async function listTools(input: ListToolsInput): Promise<MCPTool[]> {\n const principal = principalForList(input);\n const tools = await allowedTools();\n // Keep the lazy thunk per request, not per tool. Besides avoiding repeated\n // work, this gives one consistent public surface when a thunk reads a\n // dynamic source such as an environment-backed configuration.\n const publicPatterns = principal ? undefined : getPublicPatterns();\n const visible = await Promise.all(\n tools.map(async (tool) => {\n if (!passesBasePolicy(tool, principal, publicPatterns)) return false;\n return passesToolPolicy(tool, principal);\n }),\n );\n return tools.filter((_, index) => visible[index]);\n }\n\n async function callTool(input: CallToolInput): Promise<MCPResponse> {\n const args = input.arguments ?? {};\n const principal = principalForCall(input);\n const tools = await allowedTools();\n const tool = tools.find((candidate) => candidate.name === input.name);\n if (!tool) {\n throw new McpAccessError(404, `Unknown MCP tool: ${input.name}`);\n }\n\n const publicPatterns = principal ? undefined : getPublicPatterns();\n if (!passesBasePolicy(tool, principal, publicPatterns)) {\n throw new McpAccessError(\n 401,\n `Authentication is required for MCP tool: ${input.name}`,\n );\n }\n\n if (!(await passesToolPolicy(tool, principal))) {\n throw new McpAccessError(403, 'MCP tool access is not permitted.', {\n code: MCP_TOOL_ACCESS_DENIED_CODE,\n retryable: false,\n });\n }\n\n const assertion = workflowAssertions[input.name];\n if (assertion) {\n assertion(args, userForGenerator(principal) ?? null);\n }\n\n return makeGenerator(principal).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"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/protocol.ts","../src/tools.ts","../src/server.ts"],"sourcesContent":["/** 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';\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 const server = new Server(appServer.serverInfo, {\n capabilities: { tools: {} },\n });\n\n server.setRequestHandler('tools/list', async (_request, context) => ({\n tools: (await appServer.listTools({\n principal: await resolvePrincipal(options.principal, context),\n })) as Tool[],\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","/**\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 * - an optional principal-aware `toolPolicy`, used for both discovery and\n * direct calls,\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 { MCP_TOOL_ACCESS_DENIED_CODE, McpAccessError } from './errors.js';\nimport {\n classNamePrefixes,\n isAllowedCoreTool,\n isPublicToolName,\n} from './tools.js';\n\n/**\n * Generic authenticated caller information available to app-MCP policy.\n *\n * `kind`, `roles`, and `scopes` are deliberately unconstrained so an app can\n * represent a human or a scoped service without this package encoding an\n * application's identity or capability model. A missing principal means the\n * request is unauthenticated.\n */\nexport interface McpAppPrincipal {\n id?: string;\n kind?: string;\n roles?: string[];\n scopes?: string[];\n}\n\n/** Minimal legacy user shape used for generated tool-call attribution. */\nexport interface McpAppUser extends McpAppPrincipal {\n id: string;\n}\n\n/** Context supplied to the optional per-tool principal policy. */\nexport interface McpToolPolicyContext {\n principal: McpAppPrincipal | null;\n tool: MCPTool;\n}\n\n/**\n * Per-tool access policy. Return `true` to expose/allow the tool and `false`\n * to hide it from discovery and deny a direct call. A thrown error is treated\n * as a denial so policy implementation details cannot escape the app-MCP\n * boundary.\n */\nexport type McpToolPolicy = (\n context: McpToolPolicyContext,\n) => boolean | Promise<boolean>;\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 generic principal-aware tool policy. It is evaluated for every\n * tool that passes the app allow-list and base public/authenticated policy,\n * for both discovery and a direct call.\n */\n toolPolicy?: McpToolPolicy;\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 /** Caller used for public/authenticated and optional tool-policy checks. */\n principal?: McpAppPrincipal | null;\n /**\n * Backwards-compatible authenticated marker. New mounts should pass\n * `principal` so discovery and direct calls use the same identity.\n */\n authenticated?: boolean;\n}\n\n/** Tool call inputs. */\nexport interface CallToolInput {\n name: string;\n arguments?: Record<string, unknown>;\n /** Caller used for public/authenticated and optional tool-policy checks. */\n principal?: McpAppPrincipal | null;\n /**\n * Backwards-compatible user input. New callers should pass `principal`.\n * Null/undefined means unauthenticated.\n */\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 toolPolicy = options.toolPolicy;\n const workflowAssertions = options.workflowAssertions ?? {};\n\n function userForGenerator(\n principal?: McpAppPrincipal | null,\n ): McpAppUser | undefined {\n if (!principal?.id) return undefined;\n return { id: principal.id, roles: principal.roles };\n }\n\n function makeGenerator(principal?: McpAppPrincipal | null): MCPGenerator {\n const user = userForGenerator(principal);\n return new MCPGenerator(options.serverInfo as MCPConfig, {\n ...options.smrtOptions(),\n user,\n });\n }\n\n function principalForList(input: ListToolsInput): McpAppPrincipal | null {\n if (input.principal !== undefined) return input.principal;\n // Preserve callers of the original boolean API without inventing an id.\n return input.authenticated ? {} : null;\n }\n\n function principalForCall(input: CallToolInput): McpAppPrincipal | null {\n if (input.principal !== undefined) return input.principal;\n return input.user ?? null;\n }\n\n async function allowedTools(): Promise<MCPTool[]> {\n const tools = await makeGenerator().generateTools();\n return tools.filter((tool) =>\n isAllowedCoreTool(tool.name, allowedPrefixes),\n );\n }\n\n function passesBasePolicy(\n tool: MCPTool,\n principal: McpAppPrincipal | null,\n publicPatterns?: readonly string[],\n ): boolean {\n if (principal) return true;\n return isPublicToolName(tool.name, publicPatterns ?? []);\n }\n\n async function passesToolPolicy(\n tool: MCPTool,\n principal: McpAppPrincipal | null,\n ): Promise<boolean> {\n if (!toolPolicy) return true;\n try {\n return Boolean(await toolPolicy({ principal, tool }));\n } catch {\n // A policy failure must fail closed and never leak implementation detail.\n return false;\n }\n }\n\n async function listTools(input: ListToolsInput): Promise<MCPTool[]> {\n const principal = principalForList(input);\n const tools = await allowedTools();\n // Keep the lazy thunk per request, not per tool. Besides avoiding repeated\n // work, this gives one consistent public surface when a thunk reads a\n // dynamic source such as an environment-backed configuration.\n const publicPatterns = principal ? undefined : getPublicPatterns();\n const visible = await Promise.all(\n tools.map(async (tool) => {\n if (!passesBasePolicy(tool, principal, publicPatterns)) return false;\n return passesToolPolicy(tool, principal);\n }),\n );\n return tools.filter((_, index) => visible[index]);\n }\n\n async function callTool(input: CallToolInput): Promise<MCPResponse> {\n const args = input.arguments ?? {};\n const principal = principalForCall(input);\n const tools = await allowedTools();\n const tool = tools.find((candidate) => candidate.name === input.name);\n if (!tool) {\n throw new McpAccessError(404, `Unknown MCP tool: ${input.name}`);\n }\n\n const publicPatterns = principal ? undefined : getPublicPatterns();\n if (!passesBasePolicy(tool, principal, publicPatterns)) {\n throw new McpAccessError(\n 401,\n `Authentication is required for MCP tool: ${input.name}`,\n );\n }\n\n if (!(await passesToolPolicy(tool, principal))) {\n throw new McpAccessError(403, 'MCP tool access is not permitted.', {\n code: MCP_TOOL_ACCESS_DENIED_CODE,\n retryable: false,\n });\n }\n\n const assertion = workflowAssertions[input.name];\n if (assertion) {\n assertion(args, userForGenerator(principal) ?? null);\n }\n\n return makeGenerator(principal).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"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "version": "1.0.0",
3
- "timestamp": 1785905190553,
3
+ "timestamp": 1785957374902,
4
4
  "packageName": "@happyvertical/smrt-app-mcp",
5
- "packageVersion": "0.40.53",
5
+ "packageVersion": "0.40.55",
6
6
  "objects": {},
7
7
  "moduleType": "smrt",
8
8
  "smrtDependencies": [
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-08-05T04:47:06.346Z",
3
+ "generatedAt": "2026-08-05T19:16:50.502Z",
4
4
  "packageName": "@happyvertical/smrt-app-mcp",
5
- "packageVersion": "0.40.53",
5
+ "packageVersion": "0.40.55",
6
6
  "sourceManifestPath": "dist/manifest.json",
7
7
  "agentDocPath": "AGENTS.md",
8
8
  "sourceHashes": {
9
- "manifest": "cf91ebbc14bdca71d746022afb7501691ed83e72ae473fe6432404d8ecdfd646",
10
- "packageJson": "6e822322f641b9a33488f5a1edb63be4b6687c71a9271540a0362721678b4be8",
9
+ "manifest": "05faea5b8dc92d61dfa5a23d62dce73afee7309e90c455e05b52959604523504",
10
+ "packageJson": "186e4a8bd52457801d8d5219e626cc0c4ec923e09182bb73acfd1f471635c188",
11
11
  "agents": "fd1dc36d1530f81aae49e6efa2e2f5011a8a62d30816f25649d4cb597fc5662a"
12
12
  },
13
13
  "exports": [
@@ -16,7 +16,9 @@
16
16
  ],
17
17
  "dependencies": {
18
18
  "@happyvertical/smrt-core": "workspace:*",
19
- "@modelcontextprotocol/sdk": "^1.25.2",
19
+ "@modelcontextprotocol/server": "2.0.0",
20
+ "@modelcontextprotocol/conformance": "0.2.0-alpha.10",
21
+ "@modelcontextprotocol/node": "2.0.0",
20
22
  "@types/node": "24.13.2",
21
23
  "typescript": "5.9.3",
22
24
  "vite": "8.1.4",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-app-mcp",
3
- "version": "0.40.53",
3
+ "version": "0.40.55",
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",
@@ -43,10 +43,12 @@
43
43
  ],
44
44
  "author": "HappyVertical",
45
45
  "dependencies": {
46
- "@modelcontextprotocol/sdk": "^1.25.2",
47
- "@happyvertical/smrt-core": "0.40.53"
46
+ "@modelcontextprotocol/server": "2.0.0",
47
+ "@happyvertical/smrt-core": "0.40.55"
48
48
  },
49
49
  "devDependencies": {
50
+ "@modelcontextprotocol/conformance": "0.2.0-alpha.10",
51
+ "@modelcontextprotocol/node": "2.0.0",
50
52
  "@types/node": "24.13.2",
51
53
  "typescript": "5.9.3",
52
54
  "vite": "8.1.4",