@happyvertical/smrt-app-mcp 0.37.2 → 0.37.3
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/chunks/errors-DQqDD-im.js +13 -0
- package/dist/chunks/errors-DQqDD-im.js.map +1 -0
- package/dist/index.js +65 -77
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +2 -2
- package/dist/smrt-knowledge.json +7 -7
- package/dist/sveltekit.js +37 -47
- package/dist/sveltekit.js.map +1 -1
- package/package.json +5 -5
- package/dist/chunks/errors-W_2Xu9nk.js +0 -12
- package/dist/chunks/errors-W_2Xu9nk.js.map +0 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/errors.ts
|
|
2
|
+
var McpAccessError = class extends Error {
|
|
3
|
+
constructor(status, message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.status = status;
|
|
6
|
+
this.name = "McpAccessError";
|
|
7
|
+
}
|
|
8
|
+
status;
|
|
9
|
+
};
|
|
10
|
+
//#endregion
|
|
11
|
+
export { McpAccessError as t };
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=errors-DQqDD-im.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors-DQqDD-im.js","names":[],"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"],"mappings":";AAKO,IAAM,iBAAN,cAA6B,MAAM;CACxC,YACW,QACT,SACA;EACA,MAAM,OAAO;EAHJ,KAAA,SAAA;EAIT,KAAK,OAAO;CACd;CALW;AAMb"}
|
package/dist/index.js
CHANGED
|
@@ -1,90 +1,78 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as McpAccessError } from "./chunks/errors-DQqDD-im.js";
|
|
2
2
|
import { MCPGenerator } from "@happyvertical/smrt-core/generators/mcp";
|
|
3
|
+
//#region src/tools.ts
|
|
3
4
|
function matchesToolPattern(toolName, pattern) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
5
|
+
if (!pattern) return false;
|
|
6
|
+
if (pattern === "*") return true;
|
|
7
|
+
const parts = pattern.split("*");
|
|
8
|
+
if (parts.length === 1) return toolName === pattern;
|
|
9
|
+
let cursor = 0;
|
|
10
|
+
if (parts[0] && !toolName.startsWith(parts[0])) return false;
|
|
11
|
+
for (const part of parts) {
|
|
12
|
+
if (!part) continue;
|
|
13
|
+
const index = toolName.indexOf(part, cursor);
|
|
14
|
+
if (index < 0) return false;
|
|
15
|
+
cursor = index + part.length;
|
|
16
|
+
}
|
|
17
|
+
const last = parts.at(-1);
|
|
18
|
+
return !last || toolName.endsWith(last);
|
|
18
19
|
}
|
|
19
20
|
function isReadOnlyToolName(toolName) {
|
|
20
|
-
|
|
21
|
+
return toolName.endsWith("_list") || toolName.endsWith("_get");
|
|
21
22
|
}
|
|
22
23
|
function isPublicToolName(toolName, patterns) {
|
|
23
|
-
|
|
24
|
+
return isReadOnlyToolName(toolName) && patterns.some((pattern) => matchesToolPattern(toolName, pattern));
|
|
24
25
|
}
|
|
25
26
|
function classNamePrefixes(classNames) {
|
|
26
|
-
|
|
27
|
+
return new Set(classNames.map((className) => `${className.toLowerCase()}_`));
|
|
27
28
|
}
|
|
28
29
|
function isAllowedCoreTool(toolName, prefixes) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
return false;
|
|
30
|
+
for (const prefix of prefixes) if (toolName.startsWith(prefix)) return true;
|
|
31
|
+
return false;
|
|
33
32
|
}
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/server.ts
|
|
34
35
|
function createMcpAppServer(options) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
return {
|
|
76
|
-
listTools,
|
|
77
|
-
callTool,
|
|
78
|
-
serverInfo: options.serverInfo
|
|
79
|
-
};
|
|
36
|
+
const allowedPrefixes = classNamePrefixes(options.allowedClassNames);
|
|
37
|
+
const getPublicPatterns = options.publicToolPatterns ?? (() => []);
|
|
38
|
+
const workflowAssertions = options.workflowAssertions ?? {};
|
|
39
|
+
function makeGenerator(user) {
|
|
40
|
+
return new MCPGenerator(options.serverInfo, {
|
|
41
|
+
...options.smrtOptions(),
|
|
42
|
+
user: user?.id ? {
|
|
43
|
+
id: user.id,
|
|
44
|
+
roles: user.roles
|
|
45
|
+
} : void 0
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
async function listTools(input) {
|
|
49
|
+
const allowed = (await makeGenerator().generateTools()).filter((tool) => isAllowedCoreTool(tool.name, allowedPrefixes));
|
|
50
|
+
if (input.authenticated) return allowed;
|
|
51
|
+
const patterns = getPublicPatterns();
|
|
52
|
+
return allowed.filter((tool) => isPublicToolName(tool.name, patterns));
|
|
53
|
+
}
|
|
54
|
+
async function callTool(input) {
|
|
55
|
+
const args = input.arguments ?? {};
|
|
56
|
+
const user = input.user ?? null;
|
|
57
|
+
if (!(await listTools({ authenticated: true })).some((tool) => tool.name === input.name)) throw new McpAccessError(404, `Unknown MCP tool: ${input.name}`);
|
|
58
|
+
if (!user && !isPublicToolName(input.name, getPublicPatterns())) throw new McpAccessError(401, `Authentication is required for MCP tool: ${input.name}`);
|
|
59
|
+
const assertion = workflowAssertions[input.name];
|
|
60
|
+
if (assertion) assertion(args, user);
|
|
61
|
+
return makeGenerator(user).handleToolCall({
|
|
62
|
+
method: "tools/call",
|
|
63
|
+
params: {
|
|
64
|
+
arguments: args,
|
|
65
|
+
name: input.name
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
listTools,
|
|
71
|
+
callTool,
|
|
72
|
+
serverInfo: options.serverInfo
|
|
73
|
+
};
|
|
80
74
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
isAllowedCoreTool,
|
|
86
|
-
isPublicToolName,
|
|
87
|
-
isReadOnlyToolName,
|
|
88
|
-
matchesToolPattern
|
|
89
|
-
};
|
|
90
|
-
//# sourceMappingURL=index.js.map
|
|
75
|
+
//#endregion
|
|
76
|
+
export { McpAccessError, classNamePrefixes, createMcpAppServer, isAllowedCoreTool, isPublicToolName, isReadOnlyToolName, matchesToolPattern };
|
|
77
|
+
|
|
78
|
+
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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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"}
|
|
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 * - 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"],"mappings":";;;AAoBO,SAAS,mBAAmB,UAAkB,SAA0B;CAC7E,IAAI,CAAC,SAAS,OAAO;CACrB,IAAI,YAAY,KAAK,OAAO;CAE5B,MAAM,QAAQ,QAAQ,MAAM,GAAG;CAC/B,IAAI,MAAM,WAAW,GAAG,OAAO,aAAa;CAE5C,IAAI,SAAS;CACb,IAAI,MAAM,MAAM,CAAC,SAAS,WAAW,MAAM,EAAE,GAAG,OAAO;CACvD,KAAA,MAAW,QAAQ,OAAO;EACxB,IAAI,CAAC,MAAM;EACX,MAAM,QAAQ,SAAS,QAAQ,MAAM,MAAM;EAC3C,IAAI,QAAQ,GAAG,OAAO;EACtB,SAAS,QAAQ,KAAK;CACxB;CAEA,MAAM,OAAO,MAAM,GAAG,EAAE;CACxB,OAAO,CAAC,QAAQ,SAAS,SAAS,IAAI;AACxC;AAMO,SAAS,mBAAmB,UAA2B;CAC5D,OAAO,SAAS,SAAS,OAAO,KAAK,SAAS,SAAS,MAAM;AAC/D;AAOO,SAAS,iBACd,UACA,UACS;CACT,OACE,mBAAmB,QAAQ,KAC3B,SAAS,MAAM,YAAY,mBAAmB,UAAU,OAAO,CAAC;AAEpE;AAOO,SAAS,kBACd,YACqB;CACrB,OAAO,IAAI,IAAI,WAAW,KAAK,cAAc,GAAG,UAAU,YAAY,EAAC,EAAG,CAAC;AAC7E;AAMO,SAAS,kBACd,UACA,UACS;CACT,KAAA,MAAW,UAAU,UACnB,IAAI,SAAS,WAAW,MAAM,GAAG,OAAO;CAE1C,OAAO;AACT;;;AC0BO,SAAS,mBACd,SACc;CACd,MAAM,kBAAkB,kBAAkB,QAAQ,iBAAiB;CACnE,MAAM,oBACJ,QAAQ,6BAAgD,CAAC;CAC3D,MAAM,qBAAqB,QAAQ,sBAAsB,CAAC;CAE1D,SAAS,cAAc,MAAwC;EAC7D,OAAO,IAAI,aAAa,QAAQ,YAAyB;GACvD,GAAG,QAAQ,YAAY;GACvB,MAAM,MAAM,KAAK;IAAE,IAAI,KAAK;IAAI,OAAO,KAAK;GAAM,IAAI,KAAA;EACxD,CAAC;CACH;CAEA,eAAe,UAAU,OAA2C;EAElE,MAAM,WAAU,MADI,cAAc,CAAA,CAAE,cAAc,EAAA,CAC5B,QAAQ,SAC5B,kBAAkB,KAAK,MAAM,eAAe,CAC9C;EACA,IAAI,MAAM,eAAe,OAAO;EAChC,MAAM,WAAW,kBAAkB;EACnC,OAAO,QAAQ,QAAQ,SAAS,iBAAiB,KAAK,MAAM,QAAQ,CAAC;CACvE;CAEA,eAAe,SAAS,OAA4C;EAClE,MAAM,OAAO,MAAM,aAAa,CAAC;EACjC,MAAM,OAAO,MAAM,QAAQ;EAE3B,IAAI,EAAC,MADe,UAAU,EAAE,eAAe,KAAK,CAAC,EAAA,CAC1C,MAAM,SAAS,KAAK,SAAS,MAAM,IAAI,GAChD,MAAM,IAAI,eAAe,KAAK,qBAAqB,MAAM,MAAM;EAGjE,IAAI,CAAC,QAAQ,CAAC,iBAAiB,MAAM,MAAM,kBAAkB,CAAC,GAC5D,MAAM,IAAI,eACR,KACA,4CAA4C,MAAM,MACpD;EAGF,MAAM,YAAY,mBAAmB,MAAM;EAC3C,IAAI,WACF,UAAU,MAAM,IAAI;EAGtB,OAAO,cAAc,IAAI,CAAA,CAAE,eAAe;GACxC,QAAQ;GACR,QAAQ;IAAE,WAAW;IAAM,MAAM,MAAM;GAAK;EAC9C,CAAC;CACH;CAEA,OAAO;EACL;EACA;EACA,YAAY,QAAQ;CACtB;AACF"}
|
package/dist/manifest.json
CHANGED
package/dist/smrt-knowledge.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"generatedAt": "2026-07-
|
|
3
|
+
"generatedAt": "2026-07-01T20:03:15.363Z",
|
|
4
4
|
"packageName": "@happyvertical/smrt-app-mcp",
|
|
5
|
-
"packageVersion": "0.37.
|
|
5
|
+
"packageVersion": "0.37.3",
|
|
6
6
|
"sourceManifestPath": "dist/manifest.json",
|
|
7
7
|
"agentDocPath": "AGENTS.md",
|
|
8
8
|
"sourceHashes": {
|
|
9
|
-
"manifest": "
|
|
10
|
-
"packageJson": "
|
|
9
|
+
"manifest": "82194b9f6eacf11ae31561fcacaec5b4dafb7be3a1b6e26439d4d92b108dcff1",
|
|
10
|
+
"packageJson": "6cca12d03970123546f829646a85de8f3b77401ac7d4e8541e1572bbdd2b30c1",
|
|
11
11
|
"agents": "fd1dc36d1530f81aae49e6efa2e2f5011a8a62d30816f25649d4cb597fc5662a"
|
|
12
12
|
},
|
|
13
13
|
"exports": [
|
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@happyvertical/smrt-core": "workspace:*",
|
|
19
19
|
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
20
|
-
"@types/node": "
|
|
20
|
+
"@types/node": "24.13.2",
|
|
21
21
|
"typescript": "^5.9.3",
|
|
22
|
-
"vite": "^
|
|
23
|
-
"vitest": "^4.
|
|
22
|
+
"vite": "^8.1.2",
|
|
23
|
+
"vitest": "^4.1.9"
|
|
24
24
|
},
|
|
25
25
|
"smrtDependencies": [
|
|
26
26
|
"@happyvertical/smrt-core"
|
package/dist/sveltekit.js
CHANGED
|
@@ -1,53 +1,43 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { t as McpAccessError } from "./chunks/errors-DQqDD-im.js";
|
|
2
|
+
//#region src/sveltekit.ts
|
|
3
|
+
var defaultResolveUser = (event) => event.locals?.user ?? null;
|
|
4
|
+
var defaultResolveAuthenticated = (event) => Boolean(event.locals?.user);
|
|
4
5
|
function mountMcpToolsRoute(server, options = {}) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return jsonResponse({ error: error.message }, error.status);
|
|
15
|
-
}
|
|
16
|
-
throw error;
|
|
17
|
-
}
|
|
18
|
-
};
|
|
6
|
+
const resolveAuthenticated = options.resolveAuthenticated ?? defaultResolveAuthenticated;
|
|
7
|
+
return async (event) => {
|
|
8
|
+
try {
|
|
9
|
+
return jsonResponse({ tools: await server.listTools({ authenticated: resolveAuthenticated(event) }) });
|
|
10
|
+
} catch (error) {
|
|
11
|
+
if (error instanceof McpAccessError) return jsonResponse({ error: error.message }, error.status);
|
|
12
|
+
throw error;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
19
15
|
}
|
|
20
16
|
function mountMcpCallRoute(server, options = {}) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
throw error;
|
|
39
|
-
}
|
|
40
|
-
};
|
|
17
|
+
const resolveUser = options.resolveUser ?? defaultResolveUser;
|
|
18
|
+
return async (event) => {
|
|
19
|
+
const body = await event.request.json().catch(() => null);
|
|
20
|
+
if (!body?.name) return jsonResponse({ error: "name is required." }, 400);
|
|
21
|
+
const input = {
|
|
22
|
+
arguments: body.arguments ?? {},
|
|
23
|
+
name: body.name,
|
|
24
|
+
user: resolveUser(event) ?? null
|
|
25
|
+
};
|
|
26
|
+
try {
|
|
27
|
+
return jsonResponse(await server.callTool(input));
|
|
28
|
+
} catch (error) {
|
|
29
|
+
if (error instanceof McpAccessError) return jsonResponse({ error: error.message }, error.status);
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
41
33
|
}
|
|
42
34
|
function jsonResponse(body, status = 200) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
35
|
+
return new Response(JSON.stringify(body), {
|
|
36
|
+
status,
|
|
37
|
+
headers: { "content-type": "application/json" }
|
|
38
|
+
});
|
|
47
39
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
};
|
|
53
|
-
//# sourceMappingURL=sveltekit.js.map
|
|
40
|
+
//#endregion
|
|
41
|
+
export { McpAccessError, mountMcpCallRoute, mountMcpToolsRoute };
|
|
42
|
+
|
|
43
|
+
//# sourceMappingURL=sveltekit.js.map
|
package/dist/sveltekit.js.map
CHANGED
|
@@ -1 +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"],"
|
|
1
|
+
{"version":3,"file":"sveltekit.js","names":[],"sources":["../src/sveltekit.ts"],"sourcesContent":["/**\n * SvelteKit route adapters for an `McpAppServer`. Mirrors the\n * `@happyvertical/smrt-users/sveltekit` pattern: minimal `HandleInput` type\n * so we never need `@sveltejs/kit` as a real dependency.\n *\n * @packageDocumentation\n *\n * @example\n * ```ts\n * // src/routes/api/mcp/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"],"mappings":";;AAiCA,IAAM,sBAAuC,UAC1C,MAAM,QAAQ,QAAQ;AAEzB,IAAM,+BAA+B,UACnC,QAAQ,MAAM,QAAQ,IAAI;AAoBrB,SAAS,mBACd,QACA,UAAgC,CAAC,GACf;CAClB,MAAM,uBACJ,QAAQ,wBAAwB;CAElC,OAAO,OAAO,UAAU;EACtB,IAAI;GAIF,OAAO,aAAa,EAAE,OAAA,MAHF,OAAO,UAAU,EACnC,eAAe,qBAAqB,KAAK,EAC3C,CAAC,EAC2B,CAAC;EAC/B,SAAS,OAAO;GACd,IAAI,iBAAiB,gBACnB,OAAO,aAAa,EAAE,OAAO,MAAM,QAAQ,GAAG,MAAM,MAAM;GAE5D,MAAM;EACR;CACF;AACF;AAMO,SAAS,kBACd,QACA,UAAgC,CAAC,GACf;CAClB,MAAM,cAAc,QAAQ,eAAe;CAE3C,OAAO,OAAO,UAAU;EACtB,MAAM,OAAQ,MAAM,MAAM,QAAQ,KAAK,CAAA,CAAE,YAAY,IAAI;EAKzD,IAAI,CAAC,MAAM,MACT,OAAO,aAAa,EAAE,OAAO,oBAAoB,GAAG,GAAG;EAGzD,MAAM,QAAuB;GAC3B,WAAW,KAAK,aAAa,CAAC;GAC9B,MAAM,KAAK;GACX,MAAM,YAAY,KAAK,KAAK;EAC9B;EAEA,IAAI;GACF,OAAO,aAAa,MAAM,OAAO,SAAS,KAAK,CAAC;EAClD,SAAS,OAAO;GACd,IAAI,iBAAiB,gBACnB,OAAO,aAAa,EAAE,OAAO,MAAM,QAAQ,GAAG,MAAM,MAAM;GAE5D,MAAM;EACR;CACF;AACF;AAEA,SAAS,aAAa,MAAe,SAAS,KAAe;CAC3D,OAAO,IAAI,SAAS,KAAK,UAAU,IAAI,GAAG;EACxC;EACA,SAAS,EAAE,gBAAgB,mBAAmB;CAChD,CAAC;AACH"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-app-mcp",
|
|
3
|
-
"version": "0.37.
|
|
3
|
+
"version": "0.37.3",
|
|
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",
|
|
@@ -44,13 +44,13 @@
|
|
|
44
44
|
"author": "HappyVertical",
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
47
|
-
"@happyvertical/smrt-core": "0.37.
|
|
47
|
+
"@happyvertical/smrt-core": "0.37.3"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@types/node": "
|
|
50
|
+
"@types/node": "24.13.2",
|
|
51
51
|
"typescript": "^5.9.3",
|
|
52
|
-
"vite": "^
|
|
53
|
-
"vitest": "^4.
|
|
52
|
+
"vite": "^8.1.2",
|
|
53
|
+
"vitest": "^4.1.9"
|
|
54
54
|
},
|
|
55
55
|
"scripts": {
|
|
56
56
|
"build": "vite build --mode library",
|
|
@@ -1 +0,0 @@
|
|
|
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;"}
|