@happyvertical/smrt-core 0.40.61 → 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/AGENTS.md +16 -3
- package/README.md +32 -1
- package/agents/generators.md +22 -0
- package/dist/collection.d.ts +12 -1
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +37 -5
- package/dist/collection.js.map +1 -1
- package/dist/decorators/index.d.ts +8 -2
- package/dist/decorators/index.d.ts.map +1 -1
- package/dist/decorators/index.js.map +1 -1
- package/dist/embeddings/types.d.ts +4 -1
- package/dist/embeddings/types.d.ts.map +1 -1
- package/dist/generators/mcp-emit.d.ts +72 -0
- package/dist/generators/mcp-emit.d.ts.map +1 -0
- package/dist/generators/mcp-emit.js +104 -0
- package/dist/generators/mcp-emit.js.map +1 -0
- package/dist/generators/mcp-runtime-template.d.ts +5 -0
- package/dist/generators/mcp-runtime-template.d.ts.map +1 -1
- package/dist/generators/mcp-runtime-template.js +236 -3
- package/dist/generators/mcp-runtime-template.js.map +1 -1
- package/dist/generators/mcp.d.ts +63 -3
- package/dist/generators/mcp.d.ts.map +1 -1
- package/dist/generators/mcp.js +145 -21
- package/dist/generators/mcp.js.map +1 -1
- package/dist/manifest/generator.d.ts +5 -0
- package/dist/manifest/generator.d.ts.map +1 -1
- package/dist/manifest/generator.js +10 -7
- package/dist/manifest/generator.js.map +1 -1
- package/dist/manifest/static-manifest.js +1 -1
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +1 -1
- package/dist/manifest.json +1 -1
- package/dist/object.d.ts +11 -1
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +11 -1
- package/dist/object.js.map +1 -1
- package/dist/registry/types.d.ts +8 -0
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +7 -7
- package/dist/system/compatibility.d.ts.map +1 -1
- package/dist/system/compatibility.js +6 -0
- package/dist/system/compatibility.js.map +1 -1
- package/dist/system/types.d.ts +4 -1
- package/dist/system/types.d.ts.map +1 -1
- package/dist/utils/scanner-module.d.ts +7 -0
- package/dist/utils/scanner-module.d.ts.map +1 -1
- package/dist/vite-plugin/index.d.ts +7 -0
- package/dist/vite-plugin/index.d.ts.map +1 -1
- package/dist/vite-plugin/index.js +3 -2
- package/dist/vite-plugin/index.js.map +1 -1
- package/package.json +10 -12
package/dist/generators/mcp.js
CHANGED
|
@@ -2,6 +2,7 @@ import { SMRT_CUSTOM_ACTION_ERROR_METADATA_KEY, buildCustomActionInvocationArgs,
|
|
|
2
2
|
import { ObjectRegistry } from "../registry.js";
|
|
3
3
|
import { SmrtCollection } from "../collection.js";
|
|
4
4
|
import { runWithTenantGate } from "./tenant-gate.js";
|
|
5
|
+
import { generatedSiblingExtension, renderGeneratedSource, resolveGeneratedSourceLanguage } from "./mcp-emit.js";
|
|
5
6
|
import { generateClaudeConfig, generateMCPDocumentation, generateMCPScript, generateRuntimeBootstrap } from "./mcp-runtime-template.js";
|
|
6
7
|
import { buildToolInputSchema, fieldTypeToJsonSchema, finalizeMcpJsonSchema } from "./tool-schema.js";
|
|
7
8
|
import { dirname, resolve } from "node:path";
|
|
@@ -12,6 +13,19 @@ import { mkdir, writeFile } from "node:fs/promises";
|
|
|
12
13
|
*
|
|
13
14
|
* Exposes smrt objects as AI tools for Claude, GPT, and other AI models
|
|
14
15
|
*/
|
|
16
|
+
/**
|
|
17
|
+
* Write one generated module, rendering it for the requested output language.
|
|
18
|
+
*
|
|
19
|
+
* Every generator in this file produces TypeScript source; a JavaScript target
|
|
20
|
+
* is transpiled on the way out so the written file is runnable as-is (#2279).
|
|
21
|
+
*
|
|
22
|
+
* @param targetPath - Absolute path of the file to write
|
|
23
|
+
* @param source - Generated TypeScript source
|
|
24
|
+
* @param language - Language the file must be written in
|
|
25
|
+
*/
|
|
26
|
+
async function writeGeneratedFile(targetPath, source, language) {
|
|
27
|
+
await writeFile(targetPath, await renderGeneratedSource(source, language, targetPath), "utf-8");
|
|
28
|
+
}
|
|
15
29
|
var MCP_STABLE_CATALOG_TTL_MS = 864e5;
|
|
16
30
|
/**
|
|
17
31
|
* Resolve the generated tools/list cache policy at generation time.
|
|
@@ -53,6 +67,29 @@ function resolveCustomActionMethod(methods, toolAction) {
|
|
|
53
67
|
for (const [methodName, method] of methods) if (methodName.toLowerCase() === toolAction.toLowerCase()) return [methodName, method];
|
|
54
68
|
return [toolAction, void 0];
|
|
55
69
|
}
|
|
70
|
+
/** Preserve a declared method's case when a runtime-only class has no manifest. */
|
|
71
|
+
function resolveRuntimeMethodName(classConstructor, action) {
|
|
72
|
+
let prototype = classConstructor?.prototype;
|
|
73
|
+
while (prototype && prototype !== Object.prototype) {
|
|
74
|
+
const match = Object.getOwnPropertyNames(prototype).find((name) => name.toLowerCase() === action.toLowerCase());
|
|
75
|
+
if (match) return match;
|
|
76
|
+
prototype = Object.getPrototypeOf(prototype);
|
|
77
|
+
}
|
|
78
|
+
return action;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Background task methods receive `JobExecutionContext` from TaskRunner, not
|
|
82
|
+
* from untrusted MCP arguments. Keep that conventional trailing parameter out
|
|
83
|
+
* of the persisted positional call so the runner can append its live context.
|
|
84
|
+
*/
|
|
85
|
+
function buildTaskActionInvocationArgs(metadata, args) {
|
|
86
|
+
const parameters = metadata.parameters;
|
|
87
|
+
if (parameters?.at(-1)?.name === "context") return buildCustomActionInvocationArgs({
|
|
88
|
+
...metadata,
|
|
89
|
+
parameters: parameters.slice(0, -1)
|
|
90
|
+
}, args);
|
|
91
|
+
return buildCustomActionInvocationArgs(metadata, args);
|
|
92
|
+
}
|
|
56
93
|
/**
|
|
57
94
|
* Generate MCP server from smrt objects
|
|
58
95
|
*/
|
|
@@ -459,6 +496,63 @@ var MCPGenerator = class {
|
|
|
459
496
|
};
|
|
460
497
|
}
|
|
461
498
|
}
|
|
499
|
+
/** Whether a visible tool has explicitly opted into durable task execution. */
|
|
500
|
+
async supportsTaskTool(name) {
|
|
501
|
+
return await this.resolveTaskAction(name, { id: "__mcp_task_probe__" }) !== null;
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Create a durable MCP task for an explicitly enabled item custom action.
|
|
505
|
+
* The caller is responsible for checking the client's extension capability
|
|
506
|
+
* before exposing this result on the wire.
|
|
507
|
+
*/
|
|
508
|
+
async createTask(request) {
|
|
509
|
+
if (!this.context.taskStore) throw new Error("MCP Tasks is enabled but no durable task store is configured");
|
|
510
|
+
const resolved = await this.resolveTaskAction(request.params.name, request.params.arguments);
|
|
511
|
+
if (!resolved) throw new Error(`MCP task execution is not enabled for tool: ${request.params.name}`);
|
|
512
|
+
return {
|
|
513
|
+
content: [],
|
|
514
|
+
structuredContent: {},
|
|
515
|
+
resultType: "task",
|
|
516
|
+
...await this.context.taskStore.createTask({
|
|
517
|
+
objectType: resolved.objectType,
|
|
518
|
+
objectId: resolved.objectId,
|
|
519
|
+
method: resolved.methodName,
|
|
520
|
+
invocationArgs: resolved.invocationArgs,
|
|
521
|
+
tenantId: this.context.tenantId ?? null
|
|
522
|
+
})
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
async resolveTaskAction(toolName, args) {
|
|
526
|
+
const separator = toolName.indexOf("_");
|
|
527
|
+
if (separator <= 0) return null;
|
|
528
|
+
const objectPrefix = toolName.slice(0, separator);
|
|
529
|
+
const action = toolName.slice(separator + 1);
|
|
530
|
+
if ([
|
|
531
|
+
"list",
|
|
532
|
+
"get",
|
|
533
|
+
"create",
|
|
534
|
+
"update",
|
|
535
|
+
"delete"
|
|
536
|
+
].includes(action)) return null;
|
|
537
|
+
const classEntry = Array.from(ObjectRegistry.getAllClasses().entries()).find(([key, info]) => (info.name || key).toLowerCase() === objectPrefix.toLowerCase());
|
|
538
|
+
if (!classEntry) return null;
|
|
539
|
+
const [key, classInfo] = classEntry;
|
|
540
|
+
const objectName = classInfo.name || key;
|
|
541
|
+
const mcpConfig = ObjectRegistry.getConfig(objectName).mcp;
|
|
542
|
+
const configuredTasks = typeof mcpConfig === "object" ? mcpConfig.tasks : void 0;
|
|
543
|
+
if (configuredTasks !== true && (!Array.isArray(configuredTasks) || !configuredTasks.some((method) => method.toLowerCase() === action.toLowerCase()))) return null;
|
|
544
|
+
if (!(await this.generateTools()).some((tool) => tool.name === toolName)) return null;
|
|
545
|
+
const [resolvedMethodName, method] = resolveCustomActionMethod(await ObjectRegistry.getAllMethods(objectName), action);
|
|
546
|
+
const methodName = method ? resolvedMethodName : resolveRuntimeMethodName(classInfo.constructor, action);
|
|
547
|
+
const metadata = this.resolveCustomActionMetadata(objectName, methodName, method, this.hasCollectionReceiver(classInfo));
|
|
548
|
+
if (!metadata.idRequired || metadata.isStatic || typeof args.id !== "string") return null;
|
|
549
|
+
return {
|
|
550
|
+
objectType: classInfo.qualifiedName || objectName,
|
|
551
|
+
objectId: args.id,
|
|
552
|
+
methodName,
|
|
553
|
+
invocationArgs: buildTaskActionInvocationArgs(metadata, args)
|
|
554
|
+
};
|
|
555
|
+
}
|
|
462
556
|
/** Convert runtime values to the JSON values MCP structuredContent permits. */
|
|
463
557
|
toJsonValue(value) {
|
|
464
558
|
const serialized = JSON.stringify(value);
|
|
@@ -805,13 +899,14 @@ var MCPGenerator = class {
|
|
|
805
899
|
const { outputPath = ".smrt/mcp-server/index.js", serverName = this.config.name || "smrt-mcp-server", serverVersion = this.config.version || "1.0.0", debug = false, generateClaudeConfigFile = false, generateReadme = false, modular = false } = options;
|
|
806
900
|
const resolvedPath = resolve(process.cwd(), outputPath);
|
|
807
901
|
const outputDir = dirname(resolvedPath);
|
|
902
|
+
const language = resolveGeneratedSourceLanguage(resolvedPath);
|
|
808
903
|
await mkdir(outputDir, { recursive: true });
|
|
809
|
-
if (modular) await this.generateModularServer(
|
|
904
|
+
if (modular) await this.generateModularServer(resolvedPath, serverName, serverVersion, debug, language);
|
|
810
905
|
else {
|
|
811
906
|
const tools = await this.generateTools();
|
|
812
907
|
const tenantScopedObjects = await this.tenantScopedObjectNames(tools);
|
|
813
908
|
const hasTenantScopedTools = tenantScopedObjects.length > 0 || await this.hasTenantScopedTools(tools);
|
|
814
|
-
await
|
|
909
|
+
await writeGeneratedFile(resolvedPath, generateRuntimeBootstrap({
|
|
815
910
|
name: serverName,
|
|
816
911
|
version: serverVersion,
|
|
817
912
|
description: this.config.description,
|
|
@@ -820,10 +915,11 @@ var MCPGenerator = class {
|
|
|
820
915
|
debug,
|
|
821
916
|
tools,
|
|
822
917
|
customActions: await this.runtimeCustomActions(tools),
|
|
918
|
+
taskActions: await this.runtimeTaskActions(tools),
|
|
823
919
|
tenantScopedObjects,
|
|
824
920
|
stiTargets: this.runtimeStiTargets(tools),
|
|
825
921
|
toolListCacheHint: resolveMCPToolListCacheHint(this.config.cache?.toolsList, hasTenantScopedTools)
|
|
826
|
-
}),
|
|
922
|
+
}), language);
|
|
827
923
|
console.log(`✅ Generated MCP server: ${resolvedPath}`);
|
|
828
924
|
}
|
|
829
925
|
if (generateClaudeConfigFile) {
|
|
@@ -877,6 +973,26 @@ var MCPGenerator = class {
|
|
|
877
973
|
}
|
|
878
974
|
return metadata;
|
|
879
975
|
}
|
|
976
|
+
/** Emit only task-enabled item custom actions for the generated runtime. */
|
|
977
|
+
async runtimeTaskActions(tools) {
|
|
978
|
+
const actions = {};
|
|
979
|
+
const classes = ObjectRegistry.getAllClasses();
|
|
980
|
+
for (const tool of tools) {
|
|
981
|
+
if (!await this.supportsTaskTool(tool.name)) continue;
|
|
982
|
+
const separator = tool.name.indexOf("_");
|
|
983
|
+
if (separator <= 0) continue;
|
|
984
|
+
const objectPrefix = tool.name.slice(0, separator).toLowerCase();
|
|
985
|
+
const matched = Array.from(classes.entries()).find(([key, info]) => (info.name || key).toLowerCase() === objectPrefix);
|
|
986
|
+
if (!matched) continue;
|
|
987
|
+
const [key, classInfo] = matched;
|
|
988
|
+
const objectName = classInfo.name || key;
|
|
989
|
+
actions[tool.name] = {
|
|
990
|
+
objectName,
|
|
991
|
+
objectType: classInfo.qualifiedName || objectName
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
return actions;
|
|
995
|
+
}
|
|
880
996
|
/**
|
|
881
997
|
* Emit only the STI discriminator targets advertised by create-tool schemas.
|
|
882
998
|
* Generated processes start with an empty registry, so resolving the
|
|
@@ -905,30 +1021,36 @@ var MCPGenerator = class {
|
|
|
905
1021
|
* Creates separate files for tools, handlers, configuration, and main entry point.
|
|
906
1022
|
* This makes the generated server easier to customize and extend.
|
|
907
1023
|
*
|
|
908
|
-
*
|
|
1024
|
+
* The sibling modules use the entry point's own extension and the entry
|
|
1025
|
+
* emits matching relative specifiers, so its imports resolve to files that
|
|
1026
|
+
* exist and load with the same module semantics (#2279).
|
|
1027
|
+
*
|
|
1028
|
+
* @param indexPath - Absolute path of the entry point to generate
|
|
909
1029
|
* @param serverName - Server name
|
|
910
1030
|
* @param serverVersion - Server version
|
|
911
1031
|
* @param debug - Enable debug logging
|
|
1032
|
+
* @param language - Language the generated files are written in
|
|
912
1033
|
*/
|
|
913
|
-
async generateModularServer(
|
|
1034
|
+
async generateModularServer(indexPath, serverName, serverVersion, debug, language) {
|
|
1035
|
+
const outputDir = dirname(indexPath);
|
|
1036
|
+
const extension = generatedSiblingExtension(indexPath);
|
|
914
1037
|
const toolsDir = resolve(outputDir, "tools");
|
|
915
1038
|
const handlersDir = resolve(outputDir, "handlers");
|
|
916
1039
|
await mkdir(toolsDir, { recursive: true });
|
|
917
1040
|
await mkdir(handlersDir, { recursive: true });
|
|
918
|
-
const configPath = resolve(outputDir,
|
|
919
|
-
await
|
|
1041
|
+
const configPath = resolve(outputDir, `config${extension}`);
|
|
1042
|
+
await writeGeneratedFile(configPath, this.generateConfigFile(serverName, serverVersion, debug), language);
|
|
920
1043
|
console.log(`✅ Generated config: ${configPath}`);
|
|
921
1044
|
const generatedTools = await this.generateTools();
|
|
922
|
-
const toolsPath = resolve(toolsDir,
|
|
923
|
-
await
|
|
1045
|
+
const toolsPath = resolve(toolsDir, `index${extension}`);
|
|
1046
|
+
await writeGeneratedFile(toolsPath, this.generateToolsFile(generatedTools), language);
|
|
924
1047
|
console.log(`✅ Generated tools: ${toolsPath}`);
|
|
925
|
-
const handlersPath = resolve(handlersDir,
|
|
1048
|
+
const handlersPath = resolve(handlersDir, `index${extension}`);
|
|
926
1049
|
const tenantScopedObjects = await this.tenantScopedObjectNames(generatedTools);
|
|
927
1050
|
const hasTenantScopedTools = tenantScopedObjects.length > 0 || await this.hasTenantScopedTools(generatedTools);
|
|
928
|
-
await
|
|
1051
|
+
await writeGeneratedFile(handlersPath, await this.generateHandlersFile(tenantScopedObjects), language);
|
|
929
1052
|
console.log(`✅ Generated handlers: ${handlersPath}`);
|
|
930
|
-
|
|
931
|
-
await writeFile(indexPath, this.generateModularIndex(resolveMCPToolListCacheHint(this.config.cache?.toolsList, hasTenantScopedTools)), "utf-8");
|
|
1053
|
+
await writeGeneratedFile(indexPath, this.generateModularIndex(resolveMCPToolListCacheHint(this.config.cache?.toolsList, hasTenantScopedTools), extension), language);
|
|
932
1054
|
console.log(`✅ Generated MCP server: ${indexPath}`);
|
|
933
1055
|
}
|
|
934
1056
|
/**
|
|
@@ -1147,6 +1269,7 @@ const TENANT_SCOPED = new Set(${JSON.stringify(tenantScopedSet)});
|
|
|
1147
1269
|
const MCP_TENANT_ID = process.env.SMRT_MCP_TENANT_ID || undefined;
|
|
1148
1270
|
const MCP_ALLOW_CROSS_TENANT = process.env.SMRT_MCP_ALLOW_CROSS_TENANT === 'true';
|
|
1149
1271
|
` : ""}
|
|
1272
|
+
|
|
1150
1273
|
const PUBLIC_JSON_OPTIONS = {
|
|
1151
1274
|
permissions: (process.env.SMRT_MCP_PERMISSIONS || '')
|
|
1152
1275
|
.split(',')
|
|
@@ -1250,11 +1373,11 @@ function errorResult(structuredContent: any, text: string, _meta?: Record<string
|
|
|
1250
1373
|
*/
|
|
1251
1374
|
export async function handleToolCall(
|
|
1252
1375
|
name: string,
|
|
1253
|
-
|
|
1376
|
+
toolArguments: any = {},
|
|
1254
1377
|
aiConfig: any = {}
|
|
1255
1378
|
) {
|
|
1256
1379
|
try {
|
|
1257
|
-
const args =
|
|
1380
|
+
const args = toolArguments;
|
|
1258
1381
|
|
|
1259
1382
|
const runToolBody = async () => {
|
|
1260
1383
|
switch (name) {
|
|
@@ -1290,8 +1413,11 @@ ${hasTenantScoped ? `
|
|
|
1290
1413
|
}
|
|
1291
1414
|
/**
|
|
1292
1415
|
* Generate modular index file (main entry point)
|
|
1416
|
+
*
|
|
1417
|
+
* @param toolListCacheHint - Cache hint emitted for `tools/list` results
|
|
1418
|
+
* @param extension - Extension of the sibling modules this entry imports
|
|
1293
1419
|
*/
|
|
1294
|
-
generateModularIndex(toolListCacheHint) {
|
|
1420
|
+
generateModularIndex(toolListCacheHint, extension = ".js") {
|
|
1295
1421
|
return `#!/usr/bin/env node
|
|
1296
1422
|
/**
|
|
1297
1423
|
* Auto-generated MCP Server
|
|
@@ -1307,12 +1433,10 @@ import { resolve } from 'node:path';
|
|
|
1307
1433
|
import { pathToFileURL } from 'node:url';
|
|
1308
1434
|
import { ObjectRegistry } from '@happyvertical/smrt-core';
|
|
1309
1435
|
import { loadConfig } from '@happyvertical/smrt-config';
|
|
1310
|
-
import { getDatabase } from '@happyvertical/sql';
|
|
1311
|
-
import { getAI } from '@happyvertical/ai';
|
|
1312
1436
|
|
|
1313
|
-
import { SERVER_NAME, SERVER_VERSION, DEBUG } from './config
|
|
1314
|
-
import { tools } from './tools/index
|
|
1315
|
-
import { handleToolCall } from './handlers/index
|
|
1437
|
+
import { SERVER_NAME, SERVER_VERSION, DEBUG } from './config${extension}';
|
|
1438
|
+
import { tools } from './tools/index${extension}';
|
|
1439
|
+
import { handleToolCall } from './handlers/index${extension}';
|
|
1316
1440
|
|
|
1317
1441
|
const TOOL_LIST_CACHE_HINT = ${JSON.stringify(toolListCacheHint)};
|
|
1318
1442
|
|