@nocobase/ai 2.2.44 → 2.2.45

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.
@@ -6,30 +6,14 @@
6
6
  * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
- import { Logger } from '@nocobase/logger';
10
- import { AIManager } from '../ai-manager';
11
- import { MCPOptions } from '../mcp-manager';
12
- import { LoadAndRegister } from './types';
13
- import { DirectoryScanner, DirectoryScannerOptions, FileDescriptor } from './scanner';
9
+ import type { Logger } from '@nocobase/logger';
10
+ import type { MCPRegistration } from '../mcp-manager/types';
14
11
  export type MCPLoaderOptions = {
15
- pluginName: string;
16
- scan: DirectoryScannerOptions;
17
- log?: Logger;
12
+ log?: Pick<Logger, 'error'>;
18
13
  };
19
- export declare class MCPLoader extends LoadAndRegister<MCPLoaderOptions> {
20
- protected readonly ai: AIManager;
21
- protected readonly options: MCPLoaderOptions;
22
- protected readonly scanner: DirectoryScanner;
23
- protected files: FileDescriptor[];
24
- protected mcpDescriptors: MCPDescriptor[];
25
- protected log: Logger;
26
- constructor(ai: AIManager, options: MCPLoaderOptions);
27
- protected scan(): Promise<void>;
28
- protected import(): Promise<void>;
29
- protected register(): Promise<void>;
14
+ export declare class MCPLoader {
15
+ private readonly manager;
16
+ private readonly options;
17
+ constructor(manager: MCPRegistration, options?: MCPLoaderOptions);
18
+ load(): Promise<void>;
30
19
  }
31
- export type MCPDescriptor = {
32
- name: string;
33
- file: FileDescriptor;
34
- options: MCPOptions;
35
- };
package/lib/loader/mcp.js CHANGED
@@ -31,73 +31,73 @@ __export(mcp_exports, {
31
31
  });
32
32
  module.exports = __toCommonJS(mcp_exports);
33
33
  var import_utils = require("@nocobase/utils");
34
- var import_fs = require("fs");
35
- var import_types = require("./types");
36
- var import_scanner = require("./scanner");
37
- var import_utils2 = require("./utils");
38
- const _MCPLoader = class _MCPLoader extends import_types.LoadAndRegister {
39
- constructor(ai, options) {
40
- super(ai, options);
41
- this.ai = ai;
34
+ var import_promises = require("node:fs/promises");
35
+ var import_options_renderer = require("../mcp-manager/options-renderer");
36
+ const _MCPLoader = class _MCPLoader {
37
+ constructor(manager, options = {}) {
38
+ this.manager = manager;
42
39
  this.options = options;
43
- this.log = options.log;
44
- this.scanner = new import_scanner.DirectoryScanner(this.options.scan);
45
40
  }
46
- scanner;
47
- files = [];
48
- mcpDescriptors = [];
49
- log;
50
- async scan() {
51
- this.files = await this.scanner.scan();
52
- }
53
- async import() {
54
- if (!this.files.length) {
41
+ async load() {
42
+ var _a, _b, _c, _d, _e, _f;
43
+ const filePath = (0, import_utils.storagePathJoin)("ai", "mcp", "servers.json");
44
+ let content;
45
+ try {
46
+ content = await (0, import_promises.readFile)(filePath, "utf8");
47
+ } catch (error) {
48
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
49
+ return;
50
+ }
51
+ (_a = this.options.log) == null ? void 0 : _a.error(`MCP file ignored at ${filePath}: unable to read file`);
55
52
  return;
56
53
  }
57
- const descriptors = await Promise.all(
58
- this.files.map(async (file) => {
59
- var _a, _b, _c;
60
- const name = file.name;
61
- if (!(0, import_fs.existsSync)(file.path)) {
62
- (_a = this.log) == null ? void 0 : _a.error(`mcp [${name}] ignored: can not find definition file at ${file.path}`);
63
- return null;
64
- }
65
- try {
66
- const imported = await (0, import_utils.importModule)(file.path);
67
- const mod = (imported == null ? void 0 : imported.default) ?? imported;
68
- const options = typeof mod === "function" ? mod() : mod;
69
- if (!(0, import_utils2.isNonEmptyObject)(options)) {
70
- (_b = this.log) == null ? void 0 : _b.warn(`mcp [${name}] register ignored: invalid definition at ${file.path}`);
71
- return null;
72
- }
73
- return {
74
- name,
75
- file,
76
- options
77
- };
78
- } catch (e) {
79
- (_c = this.log) == null ? void 0 : _c.error(`mcp [${name}] load fail: error occur when import ${file.path}`, e);
80
- return null;
81
- }
82
- })
83
- );
84
- this.mcpDescriptors = descriptors.filter((item) => Boolean(item));
85
- }
86
- async register() {
87
- var _a;
88
- if (!this.mcpDescriptors.length) {
54
+ let definitions;
55
+ try {
56
+ definitions = JSON.parse(content);
57
+ } catch {
58
+ (_b = this.options.log) == null ? void 0 : _b.error(`MCP file ignored at ${filePath}: invalid JSON`);
59
+ return;
60
+ }
61
+ if (!Array.isArray(definitions)) {
62
+ (_c = this.options.log) == null ? void 0 : _c.error(`MCP file ignored at ${filePath}: expected an array`);
89
63
  return;
90
64
  }
91
- const { mcpManager } = this.ai;
92
- for (const descriptor of this.mcpDescriptors) {
65
+ const registration = /* @__PURE__ */ Object.create(null);
66
+ for (const [index, value] of definitions.entries()) {
67
+ const definition = value;
68
+ let reason;
69
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
70
+ reason = "expected an object";
71
+ } else if (typeof definition.name !== "string" || !definition.name.trim()) {
72
+ reason = "name is required";
73
+ } else if (!["stdio", "http", "sse"].includes(definition.transport)) {
74
+ reason = "unsupported or missing transport";
75
+ } else if (Object.hasOwn(value, "enabled") || Object.hasOwn(value, "fromFile")) {
76
+ reason = "enabled and fromFile cannot be specified";
77
+ } else if (Object.hasOwn(registration, definition.name)) {
78
+ reason = "duplicate name";
79
+ }
80
+ if (reason) {
81
+ (_d = this.options.log) == null ? void 0 : _d.error(`MCP file ignored at ${filePath}, entry ${index + 1}: ${reason}`);
82
+ return;
83
+ }
93
84
  try {
94
- await mcpManager.registerMCP({
95
- [descriptor.name]: descriptor.options
96
- });
97
- } catch (e) {
98
- (_a = this.log) == null ? void 0 : _a.error(`mcp [${descriptor.name}] register ignored: error occur when invoke registerMCP`, e);
85
+ registration[definition.name] = (0, import_options_renderer.normalizeMCPOptions)({ ...definition, fromFile: true });
86
+ } catch {
87
+ (_e = this.options.log) == null ? void 0 : _e.error(
88
+ `MCP file ignored at ${filePath}, entry ${index + 1}: unable to normalize configuration`
89
+ );
90
+ return;
99
91
  }
100
92
  }
93
+ if (!Object.keys(registration).length) {
94
+ return;
95
+ }
96
+ try {
97
+ await this.manager.registerMCP(registration);
98
+ } catch {
99
+ (_f = this.options.log) == null ? void 0 : _f.error(`MCP registration failed at ${filePath}`);
100
+ }
101
101
  }
102
102
  };
103
103
  __name(_MCPLoader, "MCPLoader");
@@ -0,0 +1,12 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ export declare class MCPToolPermissionError extends Error {
10
+ readonly code: 'INVALID_PERMISSION' | 'TOOL_NOT_FOUND_OR_AMBIGUOUS';
11
+ constructor(code: 'INVALID_PERMISSION' | 'TOOL_NOT_FOUND_OR_AMBIGUOUS');
12
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
15
+ var __export = (target, all) => {
16
+ for (var name in all)
17
+ __defProp(target, name, { get: all[name], enumerable: true });
18
+ };
19
+ var __copyProps = (to, from, except, desc) => {
20
+ if (from && typeof from === "object" || typeof from === "function") {
21
+ for (let key of __getOwnPropNames(from))
22
+ if (!__hasOwnProp.call(to, key) && key !== except)
23
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
24
+ }
25
+ return to;
26
+ };
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+ var errors_exports = {};
29
+ __export(errors_exports, {
30
+ MCPToolPermissionError: () => MCPToolPermissionError
31
+ });
32
+ module.exports = __toCommonJS(errors_exports);
33
+ const _MCPToolPermissionError = class _MCPToolPermissionError extends Error {
34
+ constructor(code) {
35
+ super(code);
36
+ this.code = code;
37
+ this.name = "MCPToolPermissionError";
38
+ }
39
+ };
40
+ __name(_MCPToolPermissionError, "MCPToolPermissionError");
41
+ let MCPToolPermissionError = _MCPToolPermissionError;
42
+ // Annotate the CommonJS export names for ESM import in node:
43
+ 0 && (module.exports = {
44
+ MCPToolPermissionError
45
+ });
@@ -17,7 +17,6 @@ export declare class DefaultMCPManager implements MCPManager {
17
17
  private mode;
18
18
  private client;
19
19
  private toolsMap;
20
- private toolsPermissionMap;
21
20
  private readonly userContextClientManager;
22
21
  constructor(app: any);
23
22
  init(): Promise<void>;
@@ -29,11 +28,11 @@ export declare class DefaultMCPManager implements MCPManager {
29
28
  rebuildClient(): Promise<void>;
30
29
  getClient(): MultiServerMCPClient | null;
31
30
  getMCPToolsProvider(): DynamicToolsProvider;
32
- private registerToolsFromMap;
33
- private ensureToolPermission;
31
+ private getActiveToolsMap;
32
+ private readToolPermissions;
33
+ private resolveToolPermission;
34
34
  listMCPTools(ctx?: Context): Promise<Record<string, MCPToolEntry[]>>;
35
- private formatMCPTools;
36
- updateMCPToolPermission(toolName: string, permission: Permission): Promise<void>;
35
+ updateMCPToolPermission(toolName: string, permission: Permission, ctx?: Context): Promise<void>;
37
36
  clearUserContextCache(): Promise<void>;
38
37
  testConnection(options: MCPOptions, ctx?: Context): Promise<MCPTestResult>;
39
38
  private buildMCPConnection;
@@ -47,3 +46,4 @@ export declare class DefaultMCPManager implements MCPManager {
47
46
  }
48
47
  export declare function defineMCP(options: MCPOptions): MCPOptions;
49
48
  export * from './types';
49
+ export * from './errors';
@@ -37,7 +37,9 @@ var import_utils = require("@nocobase/utils");
37
37
  var import_mcp_adapters = require("@langchain/mcp-adapters");
38
38
  var import_options_renderer = require("./options-renderer");
39
39
  var import_user_context_client_manager = require("./user-context-client-manager");
40
+ var import_errors = require("./errors");
40
41
  __reExport(mcp_manager_exports, require("./types"), module.exports);
42
+ __reExport(mcp_manager_exports, require("./errors"), module.exports);
41
43
  const _DefaultMCPManager = class _DefaultMCPManager {
42
44
  constructor(app) {
43
45
  this.app = app;
@@ -48,12 +50,11 @@ const _DefaultMCPManager = class _DefaultMCPManager {
48
50
  buildConnection: /* @__PURE__ */ __name((options) => this.buildMCPConnection(options), "buildConnection")
49
51
  });
50
52
  }
51
- mcpRegistry = new import_utils.Registry();
53
+ mcpRegistry = new import_utils.Registry({ override: true });
52
54
  provideCollectionManager;
53
55
  mode = "memory";
54
56
  client = null;
55
57
  toolsMap = {};
56
- toolsPermissionMap = {};
57
58
  userContextClientManager;
58
59
  async init() {
59
60
  if (this.mode === "memory") {
@@ -67,9 +68,12 @@ const _DefaultMCPManager = class _DefaultMCPManager {
67
68
  }
68
69
  }
69
70
  async registerMCP(registration) {
71
+ var _a;
70
72
  if (this.mode === "memory") {
71
73
  for (const [name, options] of Object.entries(registration)) {
72
- this.mcpRegistry.register(name, this.normalizeEntry(name, options));
74
+ const entry = this.normalizeEntry(name, options);
75
+ entry.fromFile = entry.fromFile === true || ((_a = this.mcpRegistry.get(name)) == null ? void 0 : _a.fromFile) === true;
76
+ this.mcpRegistry.register(name, entry);
73
77
  }
74
78
  } else {
75
79
  for (const [name, options] of Object.entries(registration)) {
@@ -112,8 +116,8 @@ const _DefaultMCPManager = class _DefaultMCPManager {
112
116
  } catch (e) {
113
117
  }
114
118
  this.client = null;
115
- this.toolsMap = {};
116
119
  }
120
+ this.toolsMap = {};
117
121
  const entries = await this.listMCP({ enabled: true, useUserContext: false });
118
122
  if (entries.length === 0) {
119
123
  return;
@@ -126,12 +130,6 @@ const _DefaultMCPManager = class _DefaultMCPManager {
126
130
  const toolsMap = await this.client.initializeConnections();
127
131
  for (const [serverName, tools] of Object.entries(toolsMap)) {
128
132
  this.toolsMap[serverName] = tools;
129
- for (const tool of tools) {
130
- const toolName = `mcp-${serverName}-${tool.name}`;
131
- if (!(toolName in this.toolsPermissionMap)) {
132
- this.toolsPermissionMap[toolName] = tool.name.startsWith("get") ? "ALLOW" : "ASK";
133
- }
134
- }
135
133
  }
136
134
  }
137
135
  getClient() {
@@ -139,82 +137,110 @@ const _DefaultMCPManager = class _DefaultMCPManager {
139
137
  }
140
138
  getMCPToolsProvider() {
141
139
  return async (register, filter) => {
142
- for (const [serverName, tools] of Object.entries(this.toolsMap)) {
143
- this.registerToolsFromMap(register, serverName, tools);
144
- }
145
- if (!(filter == null ? void 0 : filter.ctx)) {
146
- return;
147
- }
148
- const userToolsMap = await this.userContextClientManager.getToolsMap(filter.ctx);
149
- for (const [serverName, tools] of Object.entries(userToolsMap)) {
150
- this.registerToolsFromMap(register, serverName, tools);
140
+ const toolsMap = await this.getActiveToolsMap(filter == null ? void 0 : filter.ctx);
141
+ const permissions = await this.readToolPermissions(Object.keys(toolsMap));
142
+ for (const [serverName, tools] of Object.entries(toolsMap)) {
143
+ for (const tool of tools) {
144
+ register.registerTools({
145
+ scope: "GENERAL",
146
+ from: "mcp",
147
+ defaultPermission: this.resolveToolPermission(permissions, serverName, tool.name),
148
+ introduction: { title: tool.name, about: tool.description },
149
+ definition: {
150
+ name: `mcp-${serverName}-${tool.name}`,
151
+ description: tool.description || `MCP tool: ${tool.name} from ${serverName}`,
152
+ schema: tool.schema
153
+ },
154
+ invoke: /* @__PURE__ */ __name(async (ctx, args) => {
155
+ const entry = await this.getMCP(serverName);
156
+ if (!(entry == null ? void 0 : entry.enabled)) {
157
+ throw new Error(ctx.t("MCP server is disabled or deleted", { ns: "@nocobase/plugin-ai" }));
158
+ }
159
+ try {
160
+ return await tool.invoke(args);
161
+ } catch (error) {
162
+ return {
163
+ status: "error",
164
+ content: error instanceof Error ? error.message : "Tool invocation failed"
165
+ };
166
+ }
167
+ }, "invoke")
168
+ });
169
+ }
151
170
  }
152
171
  };
153
172
  }
154
- registerToolsFromMap(register, serverName, tools) {
155
- for (const tool of tools) {
156
- const toolName = `mcp-${serverName}-${tool.name}`;
157
- this.ensureToolPermission(toolName, tool.name);
158
- const toolOptions = {
159
- scope: "GENERAL",
160
- from: "mcp",
161
- defaultPermission: this.toolsPermissionMap[toolName],
162
- introduction: {
163
- title: tool.name,
164
- about: tool.description
165
- },
166
- definition: {
167
- name: toolName,
168
- description: tool.description || `MCP tool: ${tool.name} from ${serverName}`,
169
- schema: tool.schema
170
- },
171
- invoke: /* @__PURE__ */ __name(async (_ctx, args) => {
172
- try {
173
- const result = await tool.invoke(args);
174
- return result;
175
- } catch (error) {
176
- return {
177
- status: "error",
178
- content: (error == null ? void 0 : error.message) || "Tool invocation failed"
179
- };
180
- }
181
- }, "invoke")
182
- };
183
- register.registerTools(toolOptions);
184
- }
173
+ async getActiveToolsMap(ctx) {
174
+ const entries = await this.listMCP({ enabled: true });
175
+ const sharedServers = new Set(entries.filter((entry) => !entry.useUserContext).map((entry) => entry.name));
176
+ const userServers = new Set(entries.filter((entry) => entry.useUserContext).map((entry) => entry.name));
177
+ const userToolsMap = ctx ? await this.userContextClientManager.getToolsMap(ctx) : {};
178
+ return {
179
+ ...Object.fromEntries(Object.entries(this.toolsMap).filter(([name]) => sharedServers.has(name))),
180
+ ...Object.fromEntries(Object.entries(userToolsMap).filter(([name]) => userServers.has(name)))
181
+ };
185
182
  }
186
- ensureToolPermission(toolName, rawToolName) {
187
- if (!(toolName in this.toolsPermissionMap)) {
188
- this.toolsPermissionMap[toolName] = rawToolName.startsWith("get") ? "ALLOW" : "ASK";
183
+ async readToolPermissions(serverNames) {
184
+ if (!serverNames.length) {
185
+ return /* @__PURE__ */ new Map();
189
186
  }
187
+ const records = await this.collectionManager.getCollection("aiMcpToolPermissions").model.findAll({
188
+ where: { serverName: { [import_database.Op.in]: serverNames } }
189
+ });
190
+ return new Map(
191
+ records.map((record) => [
192
+ JSON.stringify([record.get("serverName"), record.get("toolName")]),
193
+ record.get("permission") === "ALLOW" ? "ALLOW" : "ASK"
194
+ ])
195
+ );
190
196
  }
191
- async listMCPTools(ctx) {
192
- const toolsMap = {
193
- ...this.toolsMap,
194
- ...ctx ? await this.userContextClientManager.getToolsMap(ctx) : {}
195
- };
196
- return this.formatMCPTools(toolsMap);
197
+ resolveToolPermission(permissions, serverName, toolName) {
198
+ return permissions.get(JSON.stringify([serverName, toolName])) ?? (toolName.startsWith("get") ? "ALLOW" : "ASK");
197
199
  }
198
- formatMCPTools(toolsMap) {
200
+ async listMCPTools(ctx) {
201
+ const toolsMap = await this.getActiveToolsMap(ctx);
202
+ const permissions = await this.readToolPermissions(Object.keys(toolsMap));
199
203
  return Object.fromEntries(
200
204
  Object.entries(toolsMap).map(([serverName, tools]) => [
201
205
  serverName,
202
- tools.map((tool) => {
203
- const toolName = `mcp-${serverName}-${tool.name}`;
204
- this.ensureToolPermission(toolName, tool.name);
205
- return {
206
- name: toolName,
207
- title: tool.name,
208
- description: tool.description,
209
- serverName,
210
- permission: this.toolsPermissionMap[toolName] ?? "ASK"
211
- };
212
- })
206
+ tools.map((tool) => ({
207
+ name: `mcp-${serverName}-${tool.name}`,
208
+ title: tool.name,
209
+ description: tool.description,
210
+ serverName,
211
+ permission: this.resolveToolPermission(permissions, serverName, tool.name)
212
+ }))
213
213
  ])
214
214
  );
215
215
  }
216
- async updateMCPToolPermission(toolName, permission) {
217
- this.toolsPermissionMap[toolName] = permission;
216
+ async updateMCPToolPermission(toolName, permission, ctx) {
217
+ if (typeof toolName !== "string" || !toolName.trim() || !["ASK", "ALLOW"].includes(permission)) {
218
+ throw new import_errors.MCPToolPermissionError("INVALID_PERMISSION");
219
+ }
220
+ const matches = Object.values(await this.listMCPTools(ctx)).flat().filter((tool2) => tool2.name === toolName);
221
+ if (matches.length !== 1) {
222
+ throw new import_errors.MCPToolPermissionError("TOOL_NOT_FOUND_OR_AMBIGUOUS");
223
+ }
224
+ const tool = matches[0];
225
+ await this.sequelize.transaction({ type: import_database.Transaction.TYPES.IMMEDIATE }, async (transaction) => {
226
+ const server = await this.aiMcpClientsModel.findOne({
227
+ where: { name: tool.serverName, enabled: true },
228
+ transaction,
229
+ lock: transaction.LOCK.UPDATE
230
+ });
231
+ if (!server) {
232
+ throw new import_errors.MCPToolPermissionError("TOOL_NOT_FOUND_OR_AMBIGUOUS");
233
+ }
234
+ const permissionModel = this.collectionManager.getCollection("aiMcpToolPermissions").model;
235
+ await permissionModel.upsert(
236
+ { serverName: tool.serverName, toolName: tool.title, permission },
237
+ {
238
+ transaction,
239
+ // Sequelize expects physical column names here, including when underscored is enabled.
240
+ conflictFields: ["serverName", "toolName"].map((name) => permissionModel.getAttributes()[name].field || name)
241
+ }
242
+ );
243
+ });
218
244
  }
219
245
  async clearUserContextCache() {
220
246
  await this.userContextClientManager.clear();
@@ -333,7 +359,8 @@ ${(error == null ? void 0 : error.stack) || ""}` : (error == null ? void 0 : err
333
359
  url: normalizedEntry.url,
334
360
  headers: normalizedEntry.headers,
335
361
  restart: normalizedEntry.restart,
336
- useUserContext: normalizedEntry.useUserContext
362
+ useUserContext: normalizedEntry.useUserContext,
363
+ fromFile: normalizedEntry.fromFile === true || existed.get("fromFile") === true
337
364
  },
338
365
  { transaction }
339
366
  );
@@ -382,5 +409,6 @@ __name(defineMCP, "defineMCP");
382
409
  0 && (module.exports = {
383
410
  DefaultMCPManager,
384
411
  defineMCP,
385
- ...require("./types")
412
+ ...require("./types"),
413
+ ...require("./errors")
386
414
  });
@@ -18,7 +18,7 @@ export interface MCPManager extends MCPRegistration {
18
18
  getClient(): MultiServerMCPClient | null;
19
19
  getMCPToolsProvider(): DynamicToolsProvider;
20
20
  listMCPTools(ctx?: Context): Promise<Record<string, MCPToolEntry[]>>;
21
- updateMCPToolPermission(toolName: string, permission: Permission): Promise<void>;
21
+ updateMCPToolPermission(toolName: string, permission: Permission, ctx?: Context): Promise<void>;
22
22
  clearUserContextCache(): Promise<void>;
23
23
  }
24
24
  export interface MCPRegistration {
@@ -35,9 +35,12 @@ export type MCPOptions = {
35
35
  headers?: Record<string, string>;
36
36
  restart?: Record<string, any>;
37
37
  useUserContext?: boolean;
38
+ fromFile?: boolean;
38
39
  };
39
40
  export type MCPEntry = MCPOptions & {
40
41
  name: string;
42
+ title?: string;
43
+ description?: string;
41
44
  enabled: boolean;
42
45
  };
43
46
  export type MCPFilter = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/ai",
3
- "version": "2.2.44",
3
+ "version": "2.2.45",
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.7",
19
19
  "@langchain/openai": "1.4.7",
20
- "@nocobase/data-source-manager": "2.2.44",
21
- "@nocobase/logger": "2.2.44",
22
- "@nocobase/resourcer": "2.2.44",
23
- "@nocobase/utils": "2.2.44",
20
+ "@nocobase/data-source-manager": "2.2.45",
21
+ "@nocobase/logger": "2.2.45",
22
+ "@nocobase/resourcer": "2.2.45",
23
+ "@nocobase/utils": "2.2.45",
24
24
  "d3-dsv": "2",
25
25
  "fast-glob": "^3.3.2",
26
26
  "flexsearch": "^0.8.2",
@@ -37,5 +37,5 @@
37
37
  "url": "git+https://github.com/nocobase/nocobase.git",
38
38
  "directory": "packages/ai"
39
39
  },
40
- "gitHead": "7129d0eb6df74b6fd5c77c45b4d8557c1356c9d4"
40
+ "gitHead": "41365164a6a0551491c07dc01634d8bcd1e2b7b8"
41
41
  }