@happyvertical/smrt-app-mcp 0.40.62 → 0.40.63

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
@@ -51,8 +51,13 @@ export declare function classNamePrefixes(classNames: readonly string[]): Readon
51
51
 
52
52
  /**
53
53
  * Build the app-runtime MCP server core. The returned object is intentionally
54
- * framework-agnostic: HTTP wrappers live in `@happyvertical/smrt-app-mcp/sveltekit`
55
- * and a stdio bridge lives in `@happyvertical/smrt-app-mcp/bin/smrt-mcp-bridge`.
54
+ * framework-agnostic: HTTP wrappers live in `@happyvertical/smrt-app-mcp/sveltekit`,
55
+ * this package's only other export.
56
+ *
57
+ * This package ships no stdio bridge. To pipe a deployed app's HTTP MCP surface
58
+ * to a local stdio MCP client, use `@happyvertical/smrt-app-cli`, which provides
59
+ * a generic `smrt-mcp-bridge` bin plus `runMcpStdioBridge` and
60
+ * `createAppCli().startMcpBridge()` for branded entry points.
56
61
  */
57
62
  export declare function createMcpAppServer(options: CreateMcpAppServerOptions): McpAppServer;
58
63
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/server.ts"],"sourcesContent":["/**\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 {\n isTenantScopedClassResolved,\n ObjectRegistry,\n} from '@happyvertical/smrt-core';\nimport type {\n MCPConfig,\n MCPResponse,\n MCPTool,\n} from '@happyvertical/smrt-core/generators/mcp';\nimport {\n MCP_STABLE_CATALOG_TTL_MS,\n MCPGenerator,\n} from '@happyvertical/smrt-core/generators/mcp';\nimport {\n type McpTask,\n McpTaskNotFoundError,\n McpTaskStore,\n} from '@happyvertical/smrt-jobs';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport { MCP_TOOL_ACCESS_DENIED_CODE, McpAccessError } from './errors.js';\nimport {\n classNamePrefixes,\n compareMcpToolNames,\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 /** Tenant boundary for task ownership and generated tenant-scoped actions. */\n tenantId?: string;\n /** Trusted operator override for generated tenant-scoped actions. */\n allowCrossTenant?: boolean;\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\nexport interface McpToolListCacheHint {\n ttlMs: number;\n cacheScope: 'private' | 'public';\n}\n\nexport interface McpToolListCacheOptions {\n /** Cache lifetime in milliseconds. Defaults to one day for a deploy-static catalog. */\n ttlMs?: number;\n /** Requested cache visibility. Defaults to private. */\n cacheScope?: 'private' | 'public';\n /**\n * Explicit attestation that every allowed tool is global, unauthenticated,\n * and safe to share through an intermediary cache.\n */\n publicCatalog?: true;\n}\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 * Cache policy for the MCP tools/list result. Public caching is honored only\n * when this explicitly opts in and every allowed tool is a non-tenant,\n * unauthenticated read-only tool with no principal-aware policy.\n */\n toolListCache?: McpToolListCacheOptions;\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 /** Whether this app has any explicitly enabled Tasks extension action. */\n hasTaskSupport?(): Promise<boolean>;\n /** Whether a particular visible tool is task-enabled. */\n isTaskTool?(name: string): Promise<boolean>;\n /** Static declaration used by the protocol discovery capability surface. */\n readonly tasksEnabled?: boolean;\n /** Create a durable task after applying the same tool policy as tools/call. */\n callTask?(input: CallToolInput): Promise<MCPResponse>;\n /** Principal-scoped task lifecycle operations. */\n getTask?(input: {\n taskId: string;\n principal?: McpAppPrincipal | null;\n }): Promise<McpTask>;\n updateTask?(input: {\n taskId: string;\n inputResponses: Record<string, unknown>;\n principal?: McpAppPrincipal | null;\n }): Promise<void>;\n cancelTask?(input: {\n taskId: string;\n principal?: McpAppPrincipal | null;\n }): Promise<void>;\n /** Cache policy for protocol tools/list responses. */\n getToolsListCacheHint?(): Promise<McpToolListCacheHint>;\n /** Read-only view of the configured server identity. */\n readonly serverInfo: CreateMcpAppServerOptions['serverInfo'];\n}\n\nfunction configuredToolListCacheHint(\n options: McpToolListCacheOptions | undefined,\n): McpToolListCacheHint {\n const ttlMs = options?.ttlMs ?? MCP_STABLE_CATALOG_TTL_MS;\n if (!Number.isSafeInteger(ttlMs) || ttlMs < 0) {\n throw new RangeError(\n 'MCP tools/list cache ttlMs must be a non-negative safe integer.',\n );\n }\n if (\n options?.cacheScope !== undefined &&\n options.cacheScope !== 'private' &&\n options.cacheScope !== 'public'\n ) {\n throw new RangeError(\n \"MCP tools/list cacheScope must be 'private' or 'public'.\",\n );\n }\n return {\n ttlMs,\n cacheScope:\n options?.cacheScope === 'public' && options.publicCatalog === true\n ? 'public'\n : 'private',\n };\n}\n\nfunction isTenantScopedTool(tool: MCPTool): boolean {\n const separator = tool.name.indexOf('_');\n if (separator <= 0) return false;\n const objectName = tool.name.slice(0, separator).toLowerCase();\n for (const [key, classInfo] of ObjectRegistry.getAllClasses()) {\n const name = classInfo.name || key;\n if (name.toLowerCase() === objectName) {\n return (\n ObjectRegistry.isTenantScoped(name) || isTenantScopedClassResolved(name)\n );\n }\n }\n return false;\n}\n\n/**\n * Scope task lookup to both the authenticated principal and its tenant. The\n * opaque value is stored in the existing job row, so no separate task table\n * can accidentally bypass a tenant boundary.\n */\nfunction taskOwnerIdFor(principal: McpAppPrincipal): string {\n return JSON.stringify([principal.tenantId ?? null, principal.id]);\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 const requestedToolListCacheHint = configuredToolListCacheHint(\n options.toolListCache,\n );\n const tasksEnabled = options.allowedClassNames.some((className) => {\n const mcp = ObjectRegistry.getConfig(className).mcp;\n return (\n typeof mcp === 'object' &&\n (mcp.tasks === true || (Array.isArray(mcp.tasks) && mcp.tasks.length > 0))\n );\n });\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 async function taskStoreFor(\n principal?: McpAppPrincipal | null,\n ): Promise<McpTaskStore> {\n if (!principal?.id) {\n throw new McpAccessError(\n 401,\n 'Authentication is required for MCP tasks.',\n );\n }\n const db = options.smrtOptions().db as DatabaseInterface | undefined;\n if (!db) {\n throw new Error('MCP Tasks requires smrtOptions() to provide a database');\n }\n return McpTaskStore.create(db, { ownerId: taskOwnerIdFor(principal) });\n }\n\n function makeGenerator(\n principal?: McpAppPrincipal | null,\n taskStore?: McpTaskStore,\n ): MCPGenerator {\n const user = userForGenerator(principal);\n return new MCPGenerator(options.serverInfo as MCPConfig, {\n ...options.smrtOptions(),\n user,\n tenantId: principal?.tenantId,\n allowCrossTenant: principal?.allowCrossTenant,\n ...(taskStore ? { taskStore } : {}),\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\n .filter((tool) =>\n isAllowedCoreTool(tool.name.toLowerCase(), allowedPrefixes),\n )\n .sort((left, right) => compareMcpToolNames(left.name, right.name));\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 getToolsListCacheHint(): Promise<McpToolListCacheHint> {\n if (requestedToolListCacheHint.cacheScope !== 'public') {\n return requestedToolListCacheHint;\n }\n\n // A principal-aware policy can make one caller's catalog differ from\n // another's. Likewise, tenant-scoped reads must never be shared across\n // tenants. The requested public scope is therefore honored only for a\n // complete, unauthenticated, non-tenant read-only catalog.\n const tools = await allowedTools();\n const publicPatterns = getPublicPatterns();\n const isSafePublicCatalog =\n !toolPolicy &&\n tools.every(\n (tool) =>\n isPublicToolName(tool.name, publicPatterns) &&\n !isTenantScopedTool(tool),\n );\n\n return isSafePublicCatalog\n ? requestedToolListCacheHint\n : { ...requestedToolListCacheHint, cacheScope: 'private' };\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.');\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 async function authorizeCall(input: CallToolInput): Promise<{\n args: Record<string, unknown>;\n principal: McpAppPrincipal | null;\n tool: MCPTool;\n }> {\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) throw new McpAccessError(404, 'Unknown MCP tool.');\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 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 const assertion = workflowAssertions[input.name];\n if (assertion) assertion(args, userForGenerator(principal) ?? null);\n return { args, principal, tool };\n }\n\n async function hasTaskSupport(): Promise<boolean> {\n const tools = await allowedTools();\n const generator = makeGenerator();\n for (const tool of tools) {\n if (await generator.supportsTaskTool(tool.name)) return true;\n }\n return false;\n }\n\n async function isTaskTool(name: string): Promise<boolean> {\n const tools = await allowedTools();\n if (!tools.some((tool) => tool.name === name)) return false;\n return makeGenerator().supportsTaskTool(name);\n }\n\n async function callTask(input: CallToolInput): Promise<MCPResponse> {\n const { args, principal } = await authorizeCall(input);\n const taskStore = await taskStoreFor(principal);\n return makeGenerator(principal, taskStore).createTask({\n method: 'tools/call',\n params: { arguments: args, name: input.name },\n });\n }\n\n async function withTaskStore<T>(\n principal: McpAppPrincipal | null | undefined,\n operation: (store: McpTaskStore) => Promise<T>,\n ): Promise<T> {\n try {\n return await operation(await taskStoreFor(principal));\n } catch (error) {\n if (error instanceof McpTaskNotFoundError) {\n throw new McpAccessError(404, 'Unknown MCP task.');\n }\n throw error;\n }\n }\n\n async function getTask(input: {\n taskId: string;\n principal?: McpAppPrincipal | null;\n }): Promise<McpTask> {\n return withTaskStore(input.principal, (store) =>\n store.getTask(input.taskId),\n );\n }\n\n async function updateTask(input: {\n taskId: string;\n inputResponses: Record<string, unknown>;\n principal?: McpAppPrincipal | null;\n }): Promise<void> {\n await withTaskStore(input.principal, (store) =>\n store.updateTask(input.taskId, input.inputResponses),\n );\n }\n\n async function cancelTask(input: {\n taskId: string;\n principal?: McpAppPrincipal | null;\n }): Promise<void> {\n await withTaskStore(input.principal, (store) =>\n store.cancelTask(input.taskId),\n );\n }\n\n return {\n listTools,\n callTool,\n hasTaskSupport,\n isTaskTool,\n callTask,\n getTask,\n updateTask,\n cancelTask,\n tasksEnabled,\n getToolsListCacheHint,\n serverInfo: options.serverInfo,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/server.ts"],"sourcesContent":["/**\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 {\n isTenantScopedClassResolved,\n ObjectRegistry,\n} from '@happyvertical/smrt-core';\nimport type {\n MCPConfig,\n MCPResponse,\n MCPTool,\n} from '@happyvertical/smrt-core/generators/mcp';\nimport {\n MCP_STABLE_CATALOG_TTL_MS,\n MCPGenerator,\n} from '@happyvertical/smrt-core/generators/mcp';\nimport {\n type McpTask,\n McpTaskNotFoundError,\n McpTaskStore,\n} from '@happyvertical/smrt-jobs';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport { MCP_TOOL_ACCESS_DENIED_CODE, McpAccessError } from './errors.js';\nimport {\n classNamePrefixes,\n compareMcpToolNames,\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 /** Tenant boundary for task ownership and generated tenant-scoped actions. */\n tenantId?: string;\n /** Trusted operator override for generated tenant-scoped actions. */\n allowCrossTenant?: boolean;\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\nexport interface McpToolListCacheHint {\n ttlMs: number;\n cacheScope: 'private' | 'public';\n}\n\nexport interface McpToolListCacheOptions {\n /** Cache lifetime in milliseconds. Defaults to one day for a deploy-static catalog. */\n ttlMs?: number;\n /** Requested cache visibility. Defaults to private. */\n cacheScope?: 'private' | 'public';\n /**\n * Explicit attestation that every allowed tool is global, unauthenticated,\n * and safe to share through an intermediary cache.\n */\n publicCatalog?: true;\n}\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 * Cache policy for the MCP tools/list result. Public caching is honored only\n * when this explicitly opts in and every allowed tool is a non-tenant,\n * unauthenticated read-only tool with no principal-aware policy.\n */\n toolListCache?: McpToolListCacheOptions;\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 /** Whether this app has any explicitly enabled Tasks extension action. */\n hasTaskSupport?(): Promise<boolean>;\n /** Whether a particular visible tool is task-enabled. */\n isTaskTool?(name: string): Promise<boolean>;\n /** Static declaration used by the protocol discovery capability surface. */\n readonly tasksEnabled?: boolean;\n /** Create a durable task after applying the same tool policy as tools/call. */\n callTask?(input: CallToolInput): Promise<MCPResponse>;\n /** Principal-scoped task lifecycle operations. */\n getTask?(input: {\n taskId: string;\n principal?: McpAppPrincipal | null;\n }): Promise<McpTask>;\n updateTask?(input: {\n taskId: string;\n inputResponses: Record<string, unknown>;\n principal?: McpAppPrincipal | null;\n }): Promise<void>;\n cancelTask?(input: {\n taskId: string;\n principal?: McpAppPrincipal | null;\n }): Promise<void>;\n /** Cache policy for protocol tools/list responses. */\n getToolsListCacheHint?(): Promise<McpToolListCacheHint>;\n /** Read-only view of the configured server identity. */\n readonly serverInfo: CreateMcpAppServerOptions['serverInfo'];\n}\n\nfunction configuredToolListCacheHint(\n options: McpToolListCacheOptions | undefined,\n): McpToolListCacheHint {\n const ttlMs = options?.ttlMs ?? MCP_STABLE_CATALOG_TTL_MS;\n if (!Number.isSafeInteger(ttlMs) || ttlMs < 0) {\n throw new RangeError(\n 'MCP tools/list cache ttlMs must be a non-negative safe integer.',\n );\n }\n if (\n options?.cacheScope !== undefined &&\n options.cacheScope !== 'private' &&\n options.cacheScope !== 'public'\n ) {\n throw new RangeError(\n \"MCP tools/list cacheScope must be 'private' or 'public'.\",\n );\n }\n return {\n ttlMs,\n cacheScope:\n options?.cacheScope === 'public' && options.publicCatalog === true\n ? 'public'\n : 'private',\n };\n}\n\nfunction isTenantScopedTool(tool: MCPTool): boolean {\n const separator = tool.name.indexOf('_');\n if (separator <= 0) return false;\n const objectName = tool.name.slice(0, separator).toLowerCase();\n for (const [key, classInfo] of ObjectRegistry.getAllClasses()) {\n const name = classInfo.name || key;\n if (name.toLowerCase() === objectName) {\n return (\n ObjectRegistry.isTenantScoped(name) || isTenantScopedClassResolved(name)\n );\n }\n }\n return false;\n}\n\n/**\n * Scope task lookup to both the authenticated principal and its tenant. The\n * opaque value is stored in the existing job row, so no separate task table\n * can accidentally bypass a tenant boundary.\n */\nfunction taskOwnerIdFor(principal: McpAppPrincipal): string {\n return JSON.stringify([principal.tenantId ?? null, principal.id]);\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 * this package's only other export.\n *\n * This package ships no stdio bridge. To pipe a deployed app's HTTP MCP surface\n * to a local stdio MCP client, use `@happyvertical/smrt-app-cli`, which provides\n * a generic `smrt-mcp-bridge` bin plus `runMcpStdioBridge` and\n * `createAppCli().startMcpBridge()` for branded entry points.\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 const requestedToolListCacheHint = configuredToolListCacheHint(\n options.toolListCache,\n );\n const tasksEnabled = options.allowedClassNames.some((className) => {\n const mcp = ObjectRegistry.getConfig(className).mcp;\n return (\n typeof mcp === 'object' &&\n (mcp.tasks === true || (Array.isArray(mcp.tasks) && mcp.tasks.length > 0))\n );\n });\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 async function taskStoreFor(\n principal?: McpAppPrincipal | null,\n ): Promise<McpTaskStore> {\n if (!principal?.id) {\n throw new McpAccessError(\n 401,\n 'Authentication is required for MCP tasks.',\n );\n }\n const db = options.smrtOptions().db as DatabaseInterface | undefined;\n if (!db) {\n throw new Error('MCP Tasks requires smrtOptions() to provide a database');\n }\n return McpTaskStore.create(db, { ownerId: taskOwnerIdFor(principal) });\n }\n\n function makeGenerator(\n principal?: McpAppPrincipal | null,\n taskStore?: McpTaskStore,\n ): MCPGenerator {\n const user = userForGenerator(principal);\n return new MCPGenerator(options.serverInfo as MCPConfig, {\n ...options.smrtOptions(),\n user,\n tenantId: principal?.tenantId,\n allowCrossTenant: principal?.allowCrossTenant,\n ...(taskStore ? { taskStore } : {}),\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\n .filter((tool) =>\n isAllowedCoreTool(tool.name.toLowerCase(), allowedPrefixes),\n )\n .sort((left, right) => compareMcpToolNames(left.name, right.name));\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 getToolsListCacheHint(): Promise<McpToolListCacheHint> {\n if (requestedToolListCacheHint.cacheScope !== 'public') {\n return requestedToolListCacheHint;\n }\n\n // A principal-aware policy can make one caller's catalog differ from\n // another's. Likewise, tenant-scoped reads must never be shared across\n // tenants. The requested public scope is therefore honored only for a\n // complete, unauthenticated, non-tenant read-only catalog.\n const tools = await allowedTools();\n const publicPatterns = getPublicPatterns();\n const isSafePublicCatalog =\n !toolPolicy &&\n tools.every(\n (tool) =>\n isPublicToolName(tool.name, publicPatterns) &&\n !isTenantScopedTool(tool),\n );\n\n return isSafePublicCatalog\n ? requestedToolListCacheHint\n : { ...requestedToolListCacheHint, cacheScope: 'private' };\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.');\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 async function authorizeCall(input: CallToolInput): Promise<{\n args: Record<string, unknown>;\n principal: McpAppPrincipal | null;\n tool: MCPTool;\n }> {\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) throw new McpAccessError(404, 'Unknown MCP tool.');\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 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 const assertion = workflowAssertions[input.name];\n if (assertion) assertion(args, userForGenerator(principal) ?? null);\n return { args, principal, tool };\n }\n\n async function hasTaskSupport(): Promise<boolean> {\n const tools = await allowedTools();\n const generator = makeGenerator();\n for (const tool of tools) {\n if (await generator.supportsTaskTool(tool.name)) return true;\n }\n return false;\n }\n\n async function isTaskTool(name: string): Promise<boolean> {\n const tools = await allowedTools();\n if (!tools.some((tool) => tool.name === name)) return false;\n return makeGenerator().supportsTaskTool(name);\n }\n\n async function callTask(input: CallToolInput): Promise<MCPResponse> {\n const { args, principal } = await authorizeCall(input);\n const taskStore = await taskStoreFor(principal);\n return makeGenerator(principal, taskStore).createTask({\n method: 'tools/call',\n params: { arguments: args, name: input.name },\n });\n }\n\n async function withTaskStore<T>(\n principal: McpAppPrincipal | null | undefined,\n operation: (store: McpTaskStore) => Promise<T>,\n ): Promise<T> {\n try {\n return await operation(await taskStoreFor(principal));\n } catch (error) {\n if (error instanceof McpTaskNotFoundError) {\n throw new McpAccessError(404, 'Unknown MCP task.');\n }\n throw error;\n }\n }\n\n async function getTask(input: {\n taskId: string;\n principal?: McpAppPrincipal | null;\n }): Promise<McpTask> {\n return withTaskStore(input.principal, (store) =>\n store.getTask(input.taskId),\n );\n }\n\n async function updateTask(input: {\n taskId: string;\n inputResponses: Record<string, unknown>;\n principal?: McpAppPrincipal | null;\n }): Promise<void> {\n await withTaskStore(input.principal, (store) =>\n store.updateTask(input.taskId, input.inputResponses),\n );\n }\n\n async function cancelTask(input: {\n taskId: string;\n principal?: McpAppPrincipal | null;\n }): Promise<void> {\n await withTaskStore(input.principal, (store) =>\n store.cancelTask(input.taskId),\n );\n }\n\n return {\n listTools,\n callTool,\n hasTaskSupport,\n isTaskTool,\n callTask,\n getTask,\n updateTask,\n cancelTask,\n tasksEnabled,\n getToolsListCacheHint,\n serverInfo: options.serverInfo,\n };\n}\n"],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  "version": "1.0.0",
3
3
  "timestamp": 0,
4
4
  "packageName": "@happyvertical/smrt-app-mcp",
5
- "packageVersion": "0.40.62",
5
+ "packageVersion": "0.40.63",
6
6
  "objects": {},
7
7
  "moduleType": "smrt",
8
8
  "smrtDependencies": [
@@ -2,12 +2,12 @@
2
2
  "schemaVersion": 1,
3
3
  "generatedAt": "1970-01-01T00:00:00.000Z",
4
4
  "packageName": "@happyvertical/smrt-app-mcp",
5
- "packageVersion": "0.40.62",
5
+ "packageVersion": "0.40.63",
6
6
  "sourceManifestPath": "dist/manifest.json",
7
7
  "agentDocPath": "AGENTS.md",
8
8
  "sourceHashes": {
9
- "manifest": "7b0a9d90c52e22dbb71c6448680fac49c00b58eb96c468d822e08403402c5464",
10
- "packageJson": "adf8bad9c61b71023c06d1c244b4b15943132e7e0e80fd8a585c6b3a340b6704",
9
+ "manifest": "8800ea9ffd9406f76d03edc3230b4774aac5175dd9713ea27d2c56f4cc50e465",
10
+ "packageJson": "fd2823f3326786fc4e65ca2aed1e72548634364ef52a8546de3668f113208ee1",
11
11
  "agents": "fd1dc36d1530f81aae49e6efa2e2f5011a8a62d30816f25649d4cb597fc5662a"
12
12
  },
13
13
  "exports": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-app-mcp",
3
- "version": "0.40.62",
3
+ "version": "0.40.63",
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,10 @@
43
43
  ],
44
44
  "author": "HappyVertical",
45
45
  "dependencies": {
46
- "@happyvertical/sql": "^0.85.5",
46
+ "@happyvertical/sql": "^0.86.1",
47
47
  "@modelcontextprotocol/server": "2.0.0",
48
- "@happyvertical/smrt-core": "0.40.62",
49
- "@happyvertical/smrt-jobs": "0.40.62"
48
+ "@happyvertical/smrt-core": "0.40.63",
49
+ "@happyvertical/smrt-jobs": "0.40.63"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@modelcontextprotocol/client": "2.0.0",