@happyvertical/smrt-app-mcp 0.54.2 → 0.54.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +6 -1
- package/README.md +8 -1
- package/dist/chunks/{protocol-BlbEg3jn.js → protocol-CoCuR_w7.js} +49 -15
- package/dist/chunks/protocol-CoCuR_w7.js.map +1 -0
- package/dist/defaults.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/manifest.json +3 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +5 -5
- package/dist/sveltekit.js +1 -1
- package/package.json +4 -4
- package/dist/chunks/protocol-BlbEg3jn.js.map +0 -1
package/AGENTS.md
CHANGED
|
@@ -13,7 +13,12 @@ tool policy; `./sveltekit` owns the stateless Streamable HTTP transport.
|
|
|
13
13
|
|
|
14
14
|
## Patterns
|
|
15
15
|
|
|
16
|
-
- Generate tools from core
|
|
16
|
+
- Generate tools from core scoped to the allow-list (`MCPConfig.classNames`),
|
|
17
|
+
then filter by allowed class names: a duplicate tool name among registered
|
|
18
|
+
classes the app never publishes must not fail its catalog or calls (#3490).
|
|
19
|
+
`mountMcpAppRoute`/`createDefaultMcpAppServer` resolve each model
|
|
20
|
+
constructor to its registry-qualified name, so a same-named class elsewhere
|
|
21
|
+
never collides; a raw simple name two classes share fails closed.
|
|
17
22
|
- Treat list/get tools as read-only; mutating tools require authentication unless the app deliberately wraps them with stronger policy.
|
|
18
23
|
- Keep app policy outside generated tool schemas. The wrapper owns auth, allow-list, and workflow assertion behavior.
|
|
19
24
|
- Return 404 for tools outside the app allow-list so private generated tools are not enumerated by mistake.
|
package/README.md
CHANGED
|
@@ -378,7 +378,14 @@ re-resolves credentials per request, refuses redirects, and forwards resource
|
|
|
378
378
|
and tool metadata without inventing UI or extension capabilities.
|
|
379
379
|
|
|
380
380
|
Generated tool allow-lists use the generator-owned original class identity, even
|
|
381
|
-
when the advertised name is a canonical alias.
|
|
381
|
+
when the advertised name is a canonical alias. Core generates tools only for the
|
|
382
|
+
allow-listed classes, so a duplicate tool name among registered classes the app
|
|
383
|
+
does not publish (a consumed package's models, say) cannot fail `tools/list` or
|
|
384
|
+
`tools/call`; a duplicate within the allow-list still fails closed. The
|
|
385
|
+
model-based routes (`mountMcpAppRoute`, `createDefaultMcpAppServer`) allow-list
|
|
386
|
+
each constructor's registry-qualified name, so an app model that shares a
|
|
387
|
+
simple name with another registered class gets its own tools; a raw
|
|
388
|
+
`allowedClassNames` simple name that two classes share is refused. Guards keyed by either the alias
|
|
382
389
|
or original tool name run before both direct and task dispatch. Authored workflows
|
|
383
390
|
retain their explicit names and effect policy; catalogs containing authored
|
|
384
391
|
workflows keep private cache scope because they have no generated tenant identity.
|
|
@@ -7141,12 +7141,21 @@ function configuredEffects(effects) {
|
|
|
7141
7141
|
if (!Array.isArray(effects) || effects.some((effect) => !MCP_TOOL_EFFECTS.includes(effect))) throw new TypeError("MCP effects must be an array of 'read', 'write' or 'destructive'.");
|
|
7142
7142
|
return new Set(effects);
|
|
7143
7143
|
}
|
|
7144
|
+
function sameNamedClasses(simpleName) {
|
|
7145
|
+
const wanted = simpleName.toLowerCase();
|
|
7146
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7147
|
+
const matches = [];
|
|
7148
|
+
for (const [key, info] of ObjectRegistry.getAllClasses()) {
|
|
7149
|
+
if (seen.has(info) || (info.name || key).toLowerCase() !== wanted) continue;
|
|
7150
|
+
seen.add(info);
|
|
7151
|
+
matches.push([key, info]);
|
|
7152
|
+
}
|
|
7153
|
+
return matches;
|
|
7154
|
+
}
|
|
7144
7155
|
function isTenantScopedTool(identity) {
|
|
7156
|
+
if (identity.qualifiedName) return ObjectRegistry.isTenantScoped(identity.qualifiedName) || isTenantScopedClassResolved(identity.qualifiedName);
|
|
7145
7157
|
const objectName = identity.objectName.toLowerCase();
|
|
7146
|
-
for (const [key, classInfo] of ObjectRegistry.getAllClasses())
|
|
7147
|
-
const name = classInfo.name || key;
|
|
7148
|
-
if (name.toLowerCase() === objectName) return ObjectRegistry.isTenantScoped(name) || isTenantScopedClassResolved(name);
|
|
7149
|
-
}
|
|
7158
|
+
for (const [key, classInfo] of ObjectRegistry.getAllClasses()) if ((classInfo.name || key).toLowerCase() === objectName && (ObjectRegistry.isTenantScoped(key) || isTenantScopedClassResolved(key))) return true;
|
|
7150
7159
|
return false;
|
|
7151
7160
|
}
|
|
7152
7161
|
function taskOwnerIdFor(principal) {
|
|
@@ -7160,6 +7169,7 @@ function createMcpAppServer(options) {
|
|
|
7160
7169
|
resources.set(resource.descriptor.uri, resource);
|
|
7161
7170
|
}
|
|
7162
7171
|
const allowedClassNames = new Set(options.allowedClassNames.map((name) => name.toLowerCase()));
|
|
7172
|
+
const generatorScope = Object.freeze([...allowedClassNames]);
|
|
7163
7173
|
const getPublicPatterns = options.publicToolPatterns ?? (() => []);
|
|
7164
7174
|
const toolPolicy = options.toolPolicy;
|
|
7165
7175
|
const workflowAssertions = options.workflowAssertions ?? {};
|
|
@@ -7171,8 +7181,13 @@ function createMcpAppServer(options) {
|
|
|
7171
7181
|
}
|
|
7172
7182
|
const allowedEffects = configuredEffects(options.effects);
|
|
7173
7183
|
const requestedToolListCacheHint = configuredToolListCacheHint(options.toolListCache);
|
|
7174
|
-
const tasksEnabled =
|
|
7175
|
-
|
|
7184
|
+
const tasksEnabled = [...ObjectRegistry.getAllClasses()].some(([key, classInfo]) => {
|
|
7185
|
+
if (![
|
|
7186
|
+
key,
|
|
7187
|
+
classInfo.qualifiedName,
|
|
7188
|
+
classInfo.name
|
|
7189
|
+
].some((name) => !!name && allowedClassNames.has(name.toLowerCase()))) return false;
|
|
7190
|
+
const mcp = ObjectRegistry.getConfig(key).mcp;
|
|
7176
7191
|
return typeof mcp === "object" && (mcp.tasks === true || Array.isArray(mcp.tasks) && mcp.tasks.length > 0);
|
|
7177
7192
|
});
|
|
7178
7193
|
function userForGenerator(principal) {
|
|
@@ -7194,7 +7209,10 @@ function createMcpAppServer(options) {
|
|
|
7194
7209
|
}
|
|
7195
7210
|
function makeGenerator(principal, taskStore) {
|
|
7196
7211
|
const user = userForGenerator(principal);
|
|
7197
|
-
return new MCPGenerator(
|
|
7212
|
+
return new MCPGenerator({
|
|
7213
|
+
...options.serverInfo,
|
|
7214
|
+
classNames: generatorScope
|
|
7215
|
+
}, {
|
|
7198
7216
|
...options.smrtOptions(),
|
|
7199
7217
|
user,
|
|
7200
7218
|
tenantId: principal?.tenantId,
|
|
@@ -7215,7 +7233,7 @@ function createMcpAppServer(options) {
|
|
|
7215
7233
|
const coreTools = (await generator.generateTools()).map((tool) => ({
|
|
7216
7234
|
tool,
|
|
7217
7235
|
identity: generator.getToolIdentity(tool)
|
|
7218
|
-
})).filter(({ identity }) => allowedClassNames.has(identity.objectName.toLowerCase()));
|
|
7236
|
+
})).filter(({ identity }) => allowedClassNames.has(identity.objectName.toLowerCase()) || !!identity.qualifiedName && allowedClassNames.has(identity.qualifiedName.toLowerCase()));
|
|
7219
7237
|
const names = new Set(coreTools.map(({ tool }) => tool.name));
|
|
7220
7238
|
for (const workflowTool of workflowTools) {
|
|
7221
7239
|
if (names.has(workflowTool.tool.name)) throw new Error(`Duplicate MCP tool name: ${workflowTool.tool.name}`);
|
|
@@ -7397,12 +7415,16 @@ function createMcpAppServer(options) {
|
|
|
7397
7415
|
}
|
|
7398
7416
|
function isTaskOrigin(identity, origin) {
|
|
7399
7417
|
if (identity.action.toLowerCase() !== origin.method.toLowerCase()) return false;
|
|
7400
|
-
|
|
7401
|
-
|
|
7402
|
-
|
|
7403
|
-
return origin.objectType === name
|
|
7418
|
+
if (identity.qualifiedName) {
|
|
7419
|
+
if (origin.objectType === identity.qualifiedName) return true;
|
|
7420
|
+
const info2 = ObjectRegistry.getAllClasses().get(identity.qualifiedName);
|
|
7421
|
+
return !!info2?.name && origin.objectType === info2.name && sameNamedClasses(info2.name).length === 1;
|
|
7404
7422
|
}
|
|
7405
|
-
|
|
7423
|
+
const matches = sameNamedClasses(identity.objectName);
|
|
7424
|
+
if (matches.length !== 1) return false;
|
|
7425
|
+
const [[key, info]] = matches;
|
|
7426
|
+
const name = info.name || key;
|
|
7427
|
+
return origin.objectType === name || origin.objectType === info.qualifiedName;
|
|
7406
7428
|
}
|
|
7407
7429
|
async function assertTaskToolVisible(taskId, principal) {
|
|
7408
7430
|
const origin = await taskOrigin(taskId, principal);
|
|
@@ -7473,6 +7495,18 @@ function mcpAllowedClassNames(models) {
|
|
|
7473
7495
|
}
|
|
7474
7496
|
return [...names];
|
|
7475
7497
|
}
|
|
7498
|
+
function mcpAllowedRegistryNames(models) {
|
|
7499
|
+
if (!Array.isArray(models)) throw new TypeError("MCP models must be an explicit array.");
|
|
7500
|
+
const classes = ObjectRegistry.getAllClasses();
|
|
7501
|
+
const names = /* @__PURE__ */ new Set();
|
|
7502
|
+
for (const model of models) {
|
|
7503
|
+
const registered = typeof model === "function" ? ObjectRegistry.getClassByConstructor(model) : void 0;
|
|
7504
|
+
const key = registered ? registered.qualifiedName && classes.get(registered.qualifiedName) ? registered.qualifiedName : [...classes].find(([, info]) => info === registered)?.[0] : void 0;
|
|
7505
|
+
if (!registered || !key || registered.constructor !== model) throw new TypeError("MCP models must be registered @smrt() class constructors.");
|
|
7506
|
+
names.add(key);
|
|
7507
|
+
}
|
|
7508
|
+
return [...names];
|
|
7509
|
+
}
|
|
7476
7510
|
var scopePattern = /^[\x21\x23-\x5b\x5d-\x7e]+$/u;
|
|
7477
7511
|
function snapshotStrings(values, label) {
|
|
7478
7512
|
if (!Array.isArray(values)) throw new TypeError(`MCP ${label} must be an explicit array.`);
|
|
@@ -7497,7 +7531,7 @@ function mcpPrincipalScopePolicy(options) {
|
|
|
7497
7531
|
}
|
|
7498
7532
|
function createDefaultMcpAppServer(options) {
|
|
7499
7533
|
const { models, requiredScopes, principalKinds, serverInfo, toolPolicy: narrowTool, resourcePolicy: narrowResource, taskPrincipalPolicy: narrowTask, ...rest } = options;
|
|
7500
|
-
const allowedClassNames =
|
|
7534
|
+
const allowedClassNames = mcpAllowedRegistryNames(models);
|
|
7501
7535
|
const base = mcpPrincipalScopePolicy({
|
|
7502
7536
|
requiredScopes,
|
|
7503
7537
|
principalKinds
|
|
@@ -7644,4 +7678,4 @@ function assertExtensionMetadata(value) {
|
|
|
7644
7678
|
//#endregion
|
|
7645
7679
|
export { McpAccessError as C, MCP_TOOL_ACCESS_DENIED_CODE as S, MCP_APP_RESOURCE_MAX_BYTES as _, createDefaultMcpAppServer as a, createMcpWorkflowTool as b, createMcpAppServer as c, isAllowedCoreTool as d, isPublicMcpTool as f, mcpToolEffect as g, matchesToolPattern as h, DEFAULT_MCP_APP_SERVER_INFO as i, MCP_TOOL_EFFECTS as l, isReadOnlyToolName as m, createMcpProtocolServer as n, mcpAllowedClassNames as o, isPublicToolName as p, createMcpProtocolServerForRequest as r, mcpPrincipalScopePolicy as s, MCP_TASKS_EXTENSION as t, classNamePrefixes as u, MCP_APP_RESOURCE_MIME as v, MCP_ORIGIN_DENIED_CODE as x, prepareMcpAppResource as y };
|
|
7646
7680
|
|
|
7647
|
-
//# sourceMappingURL=protocol-
|
|
7681
|
+
//# sourceMappingURL=protocol-CoCuR_w7.js.map
|