@nocobase/ai 2.1.0-alpha.13 → 2.1.0-alpha.14
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/lib/ai-employee-manager/index.js +4 -2
- package/lib/ai-employee-manager/types.d.ts +1 -0
- package/lib/loader/employee.js +21 -25
- package/lib/mcp-manager/index.js +1 -1
- package/lib/mcp-tools-manager.d.ts +19 -0
- package/lib/mcp-tools-manager.js +24 -0
- package/lib/tools-manager/types.d.ts +5 -1
- package/package.json +6 -6
- package/src/__tests__/ai-employees.test.ts +3 -0
- package/src/__tests__/resource/ai/ai-employees/index-employee/prompt.md +1 -0
- package/src/__tests__/resource/ai/ai-employees/with-tools/tools/discoveredTool.ts +1 -1
- package/src/__tests__/resource/ai/ai-employees/with-tools-merge/tools/discoveredTool.ts +1 -1
- package/src/__tests__/resource/ai/skills/data-modeling/tools/read.ts +1 -1
- package/src/__tests__/resource/ai/skills/data-modeling/tools/search/index.ts +1 -1
- package/src/__tests__/resource/ai/skills/document/tools/read.ts +1 -1
- package/src/__tests__/resource/ai/skills/document/tools/search/index.ts +1 -1
- package/src/__tests__/resource/ai/tools/desc/index.ts +1 -1
- package/src/__tests__/resource/ai/tools/group/group1.ts +1 -1
- package/src/__tests__/resource/ai/tools/group/group2.ts +1 -1
- package/src/__tests__/resource/ai/tools/group/group3/index.ts +1 -1
- package/src/__tests__/resource/ai/tools/hallow/index.ts +1 -1
- package/src/__tests__/resource/ai/tools/print.ts +1 -1
- package/src/ai-employee-manager/index.ts +2 -0
- package/src/ai-employee-manager/types.ts +1 -0
- package/src/loader/employee.ts +22 -25
- package/src/mcp-manager/index.ts +1 -1
- package/src/mcp-tools-manager.ts +50 -0
- package/src/tools-manager/types.ts +6 -1
|
@@ -109,7 +109,8 @@ const _DefaultAIEmployeeManager = class _DefaultAIEmployeeManager {
|
|
|
109
109
|
knowledgeBase: DEFAULT_KNOWLEDGE_BASE,
|
|
110
110
|
knowledgeBasePrompt: DEFAULT_KNOWLEDGE_BASE_PROMPT,
|
|
111
111
|
enabled: true,
|
|
112
|
-
builtIn: true
|
|
112
|
+
builtIn: true,
|
|
113
|
+
sort: employee.sort
|
|
113
114
|
},
|
|
114
115
|
{ transaction }
|
|
115
116
|
);
|
|
@@ -132,7 +133,8 @@ const _DefaultAIEmployeeManager = class _DefaultAIEmployeeManager {
|
|
|
132
133
|
skillSettings: {
|
|
133
134
|
skills: [...employee.skills],
|
|
134
135
|
tools: [...mergedTools]
|
|
135
|
-
}
|
|
136
|
+
},
|
|
137
|
+
sort: employee.sort
|
|
136
138
|
};
|
|
137
139
|
await existed.update(values, { transaction });
|
|
138
140
|
});
|
package/lib/loader/employee.js
CHANGED
|
@@ -44,6 +44,7 @@ module.exports = __toCommonJS(employee_exports);
|
|
|
44
44
|
var import_utils = require("@nocobase/utils");
|
|
45
45
|
var import_scanner = require("./scanner");
|
|
46
46
|
var import_fs = require("fs");
|
|
47
|
+
var import_promises = require("fs/promises");
|
|
47
48
|
var import_types = require("./types");
|
|
48
49
|
var import_path = __toESM(require("path"));
|
|
49
50
|
const _AIEmployeeLoader = class _AIEmployeeLoader extends import_types.LoadAndRegister {
|
|
@@ -67,18 +68,19 @@ const _AIEmployeeLoader = class _AIEmployeeLoader extends import_types.LoadAndRe
|
|
|
67
68
|
}
|
|
68
69
|
const grouped = /* @__PURE__ */ new Map();
|
|
69
70
|
for (const fd of this.files) {
|
|
70
|
-
const employeeRoot = getEmployeeRoot(fd
|
|
71
|
+
const employeeRoot = getEmployeeRoot(fd);
|
|
71
72
|
const group = grouped.get(employeeRoot) ?? [];
|
|
72
73
|
group.push(fd);
|
|
73
74
|
grouped.set(employeeRoot, group);
|
|
74
75
|
}
|
|
75
76
|
const descriptors = await Promise.all(
|
|
76
77
|
Array.from(grouped.entries()).map(async ([employeeRoot, fds]) => {
|
|
77
|
-
var _a, _b;
|
|
78
|
-
const file =
|
|
78
|
+
var _a, _b, _c;
|
|
79
|
+
const file = fds.find((fd) => fd.extname === ".ts" || fd.extname === ".js");
|
|
79
80
|
if (!file || !(0, import_fs.existsSync)(file.path)) {
|
|
80
81
|
return null;
|
|
81
82
|
}
|
|
83
|
+
const promptFile = fds.find((fd) => fd.basename === "prompt.md");
|
|
82
84
|
const name = import_path.default.basename(employeeRoot);
|
|
83
85
|
try {
|
|
84
86
|
const imported = await (0, import_utils.importModule)(file.path);
|
|
@@ -89,6 +91,17 @@ const _AIEmployeeLoader = class _AIEmployeeLoader extends import_types.LoadAndRe
|
|
|
89
91
|
return null;
|
|
90
92
|
}
|
|
91
93
|
const { skills = [], tools = [] } = employeeOptions;
|
|
94
|
+
if (promptFile && (0, import_fs.existsSync)(promptFile.path)) {
|
|
95
|
+
try {
|
|
96
|
+
employeeOptions.systemPrompt = await (0, import_promises.readFile)(promptFile.path, "utf-8");
|
|
97
|
+
} catch (e) {
|
|
98
|
+
(_b = this.log) == null ? void 0 : _b.error(
|
|
99
|
+
`ai employee [${name}] load fail: error occur when reading prompt.md at ${promptFile.path}`,
|
|
100
|
+
e
|
|
101
|
+
);
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
92
105
|
return {
|
|
93
106
|
name,
|
|
94
107
|
employeeRoot,
|
|
@@ -100,7 +113,7 @@ const _AIEmployeeLoader = class _AIEmployeeLoader extends import_types.LoadAndRe
|
|
|
100
113
|
}
|
|
101
114
|
};
|
|
102
115
|
} catch (e) {
|
|
103
|
-
(
|
|
116
|
+
(_c = this.log) == null ? void 0 : _c.error(`ai employee [${name}] load fail: error occur when import ${file.path}`, e);
|
|
104
117
|
return null;
|
|
105
118
|
}
|
|
106
119
|
})
|
|
@@ -119,25 +132,13 @@ const _AIEmployeeLoader = class _AIEmployeeLoader extends import_types.LoadAndRe
|
|
|
119
132
|
};
|
|
120
133
|
__name(_AIEmployeeLoader, "AIEmployeeLoader");
|
|
121
134
|
let AIEmployeeLoader = _AIEmployeeLoader;
|
|
122
|
-
function getEmployeeRoot(
|
|
123
|
-
if (
|
|
124
|
-
return import_path.default.dirname(
|
|
135
|
+
function getEmployeeRoot(fd) {
|
|
136
|
+
if (fd.basename === "index.ts" || fd.basename === "index.js" || fd.basename === "prompt.md") {
|
|
137
|
+
return import_path.default.dirname(fd.path);
|
|
125
138
|
}
|
|
126
|
-
|
|
127
|
-
return import_path.default.join(dir, name);
|
|
139
|
+
return fd.path;
|
|
128
140
|
}
|
|
129
141
|
__name(getEmployeeRoot, "getEmployeeRoot");
|
|
130
|
-
function selectEmployeeDefinitionFile(files, employeeRoot, log) {
|
|
131
|
-
const direct = files.find((fd) => !isIndexFile(fd.path));
|
|
132
|
-
const nested = files.find((fd) => isIndexFile(fd.path));
|
|
133
|
-
if (direct && nested) {
|
|
134
|
-
log == null ? void 0 : log.warn(
|
|
135
|
-
`ai employee [${import_path.default.basename(employeeRoot)}] duplicate definition found, use ${direct.path} instead of ${nested.path}`
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
return direct ?? nested;
|
|
139
|
-
}
|
|
140
|
-
__name(selectEmployeeDefinitionFile, "selectEmployeeDefinitionFile");
|
|
141
142
|
async function discoverSkills(employeeRoot) {
|
|
142
143
|
const skillsDir = import_path.default.join(employeeRoot, "skills");
|
|
143
144
|
if (!(0, import_fs.existsSync)(skillsDir)) {
|
|
@@ -171,11 +172,6 @@ async function discoverTools(employeeRoot) {
|
|
|
171
172
|
);
|
|
172
173
|
}
|
|
173
174
|
__name(discoverTools, "discoverTools");
|
|
174
|
-
function isIndexFile(filePath) {
|
|
175
|
-
const basename = import_path.default.basename(filePath);
|
|
176
|
-
return basename === "index.ts" || basename === "index.js";
|
|
177
|
-
}
|
|
178
|
-
__name(isIndexFile, "isIndexFile");
|
|
179
175
|
function uniq(values) {
|
|
180
176
|
return [...new Set(values)];
|
|
181
177
|
}
|
package/lib/mcp-manager/index.js
CHANGED
|
@@ -142,7 +142,7 @@ const _DefaultMCPManager = class _DefaultMCPManager {
|
|
|
142
142
|
description: tool.description || `MCP tool: ${tool.name} from ${serverName}`,
|
|
143
143
|
schema: tool.schema
|
|
144
144
|
},
|
|
145
|
-
invoke: /* @__PURE__ */ __name(async (_ctx, args
|
|
145
|
+
invoke: /* @__PURE__ */ __name(async (_ctx, args) => {
|
|
146
146
|
try {
|
|
147
147
|
const result = await tool.invoke(args);
|
|
148
148
|
return result;
|
|
@@ -10,15 +10,34 @@ export type McpTool = {
|
|
|
10
10
|
name: string;
|
|
11
11
|
description: string;
|
|
12
12
|
inputSchema?: any;
|
|
13
|
+
resourceName?: string;
|
|
14
|
+
actionName?: string;
|
|
15
|
+
path?: string;
|
|
16
|
+
method?: string;
|
|
13
17
|
call: (args: Record<string, any>, context?: McpToolCallContext) => Promise<any>;
|
|
14
18
|
};
|
|
15
19
|
export type McpToolCallContext = {
|
|
16
20
|
token?: string;
|
|
17
21
|
headers?: Record<string, string | string[] | undefined>;
|
|
18
22
|
};
|
|
23
|
+
export type McpToolResultPostProcessorContext = {
|
|
24
|
+
tool: McpTool;
|
|
25
|
+
args: Record<string, any>;
|
|
26
|
+
callContext?: McpToolCallContext;
|
|
27
|
+
response?: {
|
|
28
|
+
statusCode?: number;
|
|
29
|
+
headers?: Record<string, any>;
|
|
30
|
+
body?: any;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
export type McpToolResultPostProcessor = (result: any, context: McpToolResultPostProcessorContext) => any | Promise<any>;
|
|
19
34
|
export declare class McpToolsManager {
|
|
20
35
|
private tools;
|
|
36
|
+
private resultPostProcessors;
|
|
37
|
+
private getActionKey;
|
|
21
38
|
registerTools(tools: McpTool[]): void;
|
|
39
|
+
registerToolResultPostProcessor(resourceName: string, actionName: string, processor: McpToolResultPostProcessor): void;
|
|
40
|
+
postProcessToolResult(tool: McpTool, result: any, context: Omit<McpToolResultPostProcessorContext, 'tool'>): Promise<any>;
|
|
22
41
|
listTools(): McpTool[];
|
|
23
42
|
getTool(name: string): McpTool;
|
|
24
43
|
}
|
package/lib/mcp-tools-manager.js
CHANGED
|
@@ -33,11 +33,35 @@ module.exports = __toCommonJS(mcp_tools_manager_exports);
|
|
|
33
33
|
var import_utils = require("@nocobase/utils");
|
|
34
34
|
const _McpToolsManager = class _McpToolsManager {
|
|
35
35
|
tools = new import_utils.Registry();
|
|
36
|
+
resultPostProcessors = /* @__PURE__ */ new Map();
|
|
37
|
+
getActionKey(resourceName, actionName) {
|
|
38
|
+
return `${resourceName}:${actionName}`;
|
|
39
|
+
}
|
|
36
40
|
registerTools(tools) {
|
|
37
41
|
for (const tool of tools) {
|
|
38
42
|
this.tools.register(tool.name, tool);
|
|
39
43
|
}
|
|
40
44
|
}
|
|
45
|
+
registerToolResultPostProcessor(resourceName, actionName, processor) {
|
|
46
|
+
const key = this.getActionKey(resourceName, actionName);
|
|
47
|
+
const processors = this.resultPostProcessors.get(key) || [];
|
|
48
|
+
processors.push(processor);
|
|
49
|
+
this.resultPostProcessors.set(key, processors);
|
|
50
|
+
}
|
|
51
|
+
async postProcessToolResult(tool, result, context) {
|
|
52
|
+
if (!tool.resourceName || !tool.actionName) {
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
const processors = this.resultPostProcessors.get(this.getActionKey(tool.resourceName, tool.actionName)) || [];
|
|
56
|
+
let current = result;
|
|
57
|
+
for (const processor of processors) {
|
|
58
|
+
current = await processor(current, {
|
|
59
|
+
...context,
|
|
60
|
+
tool
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return current;
|
|
64
|
+
}
|
|
41
65
|
listTools() {
|
|
42
66
|
return [...this.tools.getValues()];
|
|
43
67
|
}
|
|
@@ -30,7 +30,11 @@ export type ToolsOptions = {
|
|
|
30
30
|
description: string;
|
|
31
31
|
schema?: any;
|
|
32
32
|
};
|
|
33
|
-
invoke: (ctx: Context, args: any,
|
|
33
|
+
invoke: (ctx: Context, args: any, runtime: ToolsRuntime) => Promise<any>;
|
|
34
|
+
};
|
|
35
|
+
export type ToolsRuntime = {
|
|
36
|
+
toolCallId: string;
|
|
37
|
+
writer: (chunk: any) => void;
|
|
34
38
|
};
|
|
35
39
|
export type ToolsEntry = ToolsOptions;
|
|
36
40
|
export type Scope = 'SPECIFIED' | 'GENERAL' | 'CUSTOM';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/ai",
|
|
3
|
-
"version": "2.1.0-alpha.
|
|
3
|
+
"version": "2.1.0-alpha.14",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
"@langchain/mcp-adapters": "^1.1.3",
|
|
18
18
|
"@langchain/ollama": "^1.2.2",
|
|
19
19
|
"@langchain/openai": "^1.2.7",
|
|
20
|
-
"@nocobase/data-source-manager": "2.1.0-alpha.
|
|
21
|
-
"@nocobase/logger": "2.1.0-alpha.
|
|
22
|
-
"@nocobase/resourcer": "2.1.0-alpha.
|
|
23
|
-
"@nocobase/utils": "2.1.0-alpha.
|
|
20
|
+
"@nocobase/data-source-manager": "2.1.0-alpha.14",
|
|
21
|
+
"@nocobase/logger": "2.1.0-alpha.14",
|
|
22
|
+
"@nocobase/resourcer": "2.1.0-alpha.14",
|
|
23
|
+
"@nocobase/utils": "2.1.0-alpha.14",
|
|
24
24
|
"fast-glob": "^3.3.2",
|
|
25
25
|
"flexsearch": "^0.8.2",
|
|
26
26
|
"gray-matter": "^4.0.3",
|
|
@@ -35,5 +35,5 @@
|
|
|
35
35
|
"url": "git+https://github.com/nocobase/nocobase.git",
|
|
36
36
|
"directory": "packages/ai"
|
|
37
37
|
},
|
|
38
|
-
"gitHead": "
|
|
38
|
+
"gitHead": "d8735b541de0ff9557bba704de49c799b4962672"
|
|
39
39
|
}
|
|
@@ -13,6 +13,7 @@ import path from 'path';
|
|
|
13
13
|
import { AIManager } from '../ai-manager';
|
|
14
14
|
import { AIEmployeeManager, AIEmployeeEntry } from '../ai-employee-manager';
|
|
15
15
|
|
|
16
|
+
const normalizeEOL = (value: string) => value.replace(/\r\n?/g, '\n');
|
|
16
17
|
const normalizeTools = (entry: AIEmployeeEntry) =>
|
|
17
18
|
[...(entry.skillSettings?.tools ?? [])].sort((a, b) => a.name.localeCompare(b.name));
|
|
18
19
|
const normalizeSkills = (entry: AIEmployeeEntry) => [...(entry.skillSettings?.skills ?? [])].sort();
|
|
@@ -39,6 +40,7 @@ describe('AI employee loader test cases', () => {
|
|
|
39
40
|
'**/ai-employees/*/index.ts',
|
|
40
41
|
'**/ai-employees/*.js',
|
|
41
42
|
'**/ai-employees/*/index.js',
|
|
43
|
+
'**/ai-employees/*/prompt.md',
|
|
42
44
|
'!**/ai-employees/**/*.d.ts',
|
|
43
45
|
],
|
|
44
46
|
},
|
|
@@ -67,6 +69,7 @@ describe('AI employee loader test cases', () => {
|
|
|
67
69
|
expect(employee).toBeDefined();
|
|
68
70
|
expect(employee.username).toBe('index-employee');
|
|
69
71
|
expect(employee.nickname).toBe('Index Employee');
|
|
72
|
+
expect(normalizeEOL(employee.defaultPrompt)).toBe('Prompt from markdown file.\n');
|
|
70
73
|
expect(normalizeTools(employee)).toEqual([]);
|
|
71
74
|
expect(normalizeSkills(employee)).toEqual([]);
|
|
72
75
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Prompt from markdown file.
|
|
@@ -96,6 +96,7 @@ export class DefaultAIEmployeeManager implements AIEmployeeManager {
|
|
|
96
96
|
knowledgeBasePrompt: DEFAULT_KNOWLEDGE_BASE_PROMPT,
|
|
97
97
|
enabled: true,
|
|
98
98
|
builtIn: true,
|
|
99
|
+
sort: employee.sort,
|
|
99
100
|
},
|
|
100
101
|
{ transaction },
|
|
101
102
|
);
|
|
@@ -117,6 +118,7 @@ export class DefaultAIEmployeeManager implements AIEmployeeManager {
|
|
|
117
118
|
skills: [...employee.skills],
|
|
118
119
|
tools: [...mergedTools],
|
|
119
120
|
},
|
|
121
|
+
sort: employee.sort,
|
|
120
122
|
};
|
|
121
123
|
await existed.update(values, { transaction });
|
|
122
124
|
});
|
package/src/loader/employee.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { importModule } from '@nocobase/utils';
|
|
11
11
|
import { DirectoryScanner, DirectoryScannerOptions, FileDescriptor } from './scanner';
|
|
12
12
|
import { existsSync } from 'fs';
|
|
13
|
+
import { readFile } from 'fs/promises';
|
|
13
14
|
import { AIManager } from '../ai-manager';
|
|
14
15
|
import { LoadAndRegister } from './types';
|
|
15
16
|
import { Logger } from '@nocobase/logger';
|
|
@@ -45,7 +46,7 @@ export class AIEmployeeLoader extends LoadAndRegister<AIEmployeeLoaderOptions> {
|
|
|
45
46
|
|
|
46
47
|
const grouped = new Map<string, FileDescriptor[]>();
|
|
47
48
|
for (const fd of this.files) {
|
|
48
|
-
const employeeRoot = getEmployeeRoot(fd
|
|
49
|
+
const employeeRoot = getEmployeeRoot(fd);
|
|
49
50
|
const group = grouped.get(employeeRoot) ?? [];
|
|
50
51
|
group.push(fd);
|
|
51
52
|
grouped.set(employeeRoot, group);
|
|
@@ -53,10 +54,12 @@ export class AIEmployeeLoader extends LoadAndRegister<AIEmployeeLoaderOptions> {
|
|
|
53
54
|
|
|
54
55
|
const descriptors = await Promise.all(
|
|
55
56
|
Array.from(grouped.entries()).map(async ([employeeRoot, fds]) => {
|
|
56
|
-
const file =
|
|
57
|
+
const file = fds.find((fd) => fd.extname === '.ts' || fd.extname === '.js');
|
|
57
58
|
if (!file || !existsSync(file.path)) {
|
|
58
59
|
return null;
|
|
59
60
|
}
|
|
61
|
+
const promptFile = fds.find((fd) => fd.basename === 'prompt.md');
|
|
62
|
+
|
|
60
63
|
const name = path.basename(employeeRoot);
|
|
61
64
|
try {
|
|
62
65
|
const imported = await importModule(file.path);
|
|
@@ -67,6 +70,19 @@ export class AIEmployeeLoader extends LoadAndRegister<AIEmployeeLoaderOptions> {
|
|
|
67
70
|
return null;
|
|
68
71
|
}
|
|
69
72
|
const { skills = [], tools = [] } = employeeOptions;
|
|
73
|
+
|
|
74
|
+
if (promptFile && existsSync(promptFile.path)) {
|
|
75
|
+
try {
|
|
76
|
+
employeeOptions.systemPrompt = await readFile(promptFile.path, 'utf-8');
|
|
77
|
+
} catch (e) {
|
|
78
|
+
this.log?.error(
|
|
79
|
+
`ai employee [${name}] load fail: error occur when reading prompt.md at ${promptFile.path}`,
|
|
80
|
+
e,
|
|
81
|
+
);
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
70
86
|
return {
|
|
71
87
|
name,
|
|
72
88
|
employeeRoot,
|
|
@@ -105,25 +121,11 @@ export type AIEmployeeDescriptor = {
|
|
|
105
121
|
options: AIEmployeeOptions;
|
|
106
122
|
};
|
|
107
123
|
|
|
108
|
-
function getEmployeeRoot(
|
|
109
|
-
if (
|
|
110
|
-
return path.dirname(
|
|
111
|
-
}
|
|
112
|
-
const { dir, name } = path.parse(filePath);
|
|
113
|
-
return path.join(dir, name);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function selectEmployeeDefinitionFile(files: FileDescriptor[], employeeRoot: string, log?: Logger) {
|
|
117
|
-
const direct = files.find((fd) => !isIndexFile(fd.path));
|
|
118
|
-
const nested = files.find((fd) => isIndexFile(fd.path));
|
|
119
|
-
if (direct && nested) {
|
|
120
|
-
log?.warn(
|
|
121
|
-
`ai employee [${path.basename(employeeRoot)}] duplicate definition found, use ${direct.path} instead of ${
|
|
122
|
-
nested.path
|
|
123
|
-
}`,
|
|
124
|
-
);
|
|
124
|
+
function getEmployeeRoot(fd: FileDescriptor) {
|
|
125
|
+
if (fd.basename === 'index.ts' || fd.basename === 'index.js' || fd.basename === 'prompt.md') {
|
|
126
|
+
return path.dirname(fd.path);
|
|
125
127
|
}
|
|
126
|
-
return
|
|
128
|
+
return fd.path;
|
|
127
129
|
}
|
|
128
130
|
|
|
129
131
|
async function discoverSkills(employeeRoot: string): Promise<string[]> {
|
|
@@ -161,11 +163,6 @@ async function discoverTools(employeeRoot: string): Promise<AIEmployeeToolSettin
|
|
|
161
163
|
);
|
|
162
164
|
}
|
|
163
165
|
|
|
164
|
-
function isIndexFile(filePath: string) {
|
|
165
|
-
const basename = path.basename(filePath);
|
|
166
|
-
return basename === 'index.ts' || basename === 'index.js';
|
|
167
|
-
}
|
|
168
|
-
|
|
169
166
|
function uniq<T>(values: T[]) {
|
|
170
167
|
return [...new Set(values)];
|
|
171
168
|
}
|
package/src/mcp-manager/index.ts
CHANGED
|
@@ -139,7 +139,7 @@ export class DefaultMCPManager implements MCPManager {
|
|
|
139
139
|
description: tool.description || `MCP tool: ${tool.name} from ${serverName}`,
|
|
140
140
|
schema: tool.schema,
|
|
141
141
|
},
|
|
142
|
-
invoke: async (_ctx: Context, args: any
|
|
142
|
+
invoke: async (_ctx: Context, args: any) => {
|
|
143
143
|
try {
|
|
144
144
|
const result = await tool.invoke(args);
|
|
145
145
|
return result;
|
package/src/mcp-tools-manager.ts
CHANGED
|
@@ -13,6 +13,10 @@ export type McpTool = {
|
|
|
13
13
|
name: string;
|
|
14
14
|
description: string;
|
|
15
15
|
inputSchema?: any;
|
|
16
|
+
resourceName?: string;
|
|
17
|
+
actionName?: string;
|
|
18
|
+
path?: string;
|
|
19
|
+
method?: string;
|
|
16
20
|
call: (args: Record<string, any>, context?: McpToolCallContext) => Promise<any>;
|
|
17
21
|
};
|
|
18
22
|
|
|
@@ -21,8 +25,29 @@ export type McpToolCallContext = {
|
|
|
21
25
|
headers?: Record<string, string | string[] | undefined>;
|
|
22
26
|
};
|
|
23
27
|
|
|
28
|
+
export type McpToolResultPostProcessorContext = {
|
|
29
|
+
tool: McpTool;
|
|
30
|
+
args: Record<string, any>;
|
|
31
|
+
callContext?: McpToolCallContext;
|
|
32
|
+
response?: {
|
|
33
|
+
statusCode?: number;
|
|
34
|
+
headers?: Record<string, any>;
|
|
35
|
+
body?: any;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type McpToolResultPostProcessor = (
|
|
40
|
+
result: any,
|
|
41
|
+
context: McpToolResultPostProcessorContext,
|
|
42
|
+
) => any | Promise<any>;
|
|
43
|
+
|
|
24
44
|
export class McpToolsManager {
|
|
25
45
|
private tools = new Registry<McpTool>();
|
|
46
|
+
private resultPostProcessors = new Map<string, McpToolResultPostProcessor[]>();
|
|
47
|
+
|
|
48
|
+
private getActionKey(resourceName: string, actionName: string) {
|
|
49
|
+
return `${resourceName}:${actionName}`;
|
|
50
|
+
}
|
|
26
51
|
|
|
27
52
|
registerTools(tools: McpTool[]) {
|
|
28
53
|
for (const tool of tools) {
|
|
@@ -30,6 +55,31 @@ export class McpToolsManager {
|
|
|
30
55
|
}
|
|
31
56
|
}
|
|
32
57
|
|
|
58
|
+
registerToolResultPostProcessor(resourceName: string, actionName: string, processor: McpToolResultPostProcessor) {
|
|
59
|
+
const key = this.getActionKey(resourceName, actionName);
|
|
60
|
+
const processors = this.resultPostProcessors.get(key) || [];
|
|
61
|
+
processors.push(processor);
|
|
62
|
+
this.resultPostProcessors.set(key, processors);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async postProcessToolResult(tool: McpTool, result: any, context: Omit<McpToolResultPostProcessorContext, 'tool'>) {
|
|
66
|
+
if (!tool.resourceName || !tool.actionName) {
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const processors = this.resultPostProcessors.get(this.getActionKey(tool.resourceName, tool.actionName)) || [];
|
|
71
|
+
let current = result;
|
|
72
|
+
|
|
73
|
+
for (const processor of processors) {
|
|
74
|
+
current = await processor(current, {
|
|
75
|
+
...context,
|
|
76
|
+
tool,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return current;
|
|
81
|
+
}
|
|
82
|
+
|
|
33
83
|
listTools() {
|
|
34
84
|
return [...this.tools.getValues()];
|
|
35
85
|
}
|
|
@@ -34,7 +34,12 @@ export type ToolsOptions = {
|
|
|
34
34
|
description: string;
|
|
35
35
|
schema?: any;
|
|
36
36
|
};
|
|
37
|
-
invoke: (ctx: Context, args: any,
|
|
37
|
+
invoke: (ctx: Context, args: any, runtime: ToolsRuntime) => Promise<any>;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type ToolsRuntime = {
|
|
41
|
+
toolCallId: string;
|
|
42
|
+
writer: (chunk: any) => void;
|
|
38
43
|
};
|
|
39
44
|
|
|
40
45
|
export type ToolsEntry = ToolsOptions;
|