Skip to content

Commit 404d218

Browse files
FinleyGec121914yu
authored andcommitted
perf: system toolset & mcp (#5200)
* feat: support system toolset * fix: type * fix: system tool config * chore: mcptool config migrate * refactor: mcp toolset * fix: fe type error * fix: type error * fix: show version * chore: support extract tool's secretInputConfig out of inputs * chore: compatible with old version mcp * chore: adjust * deps: update dependency @fastgpt-skd/plugin * fix: version
1 parent a45d933 commit 404d218

File tree

32 files changed

+582
-392
lines changed

32 files changed

+582
-392
lines changed

packages/global/core/app/mcpTools/utils.ts

Lines changed: 31 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,38 +14,37 @@ import { type RuntimeNodeItemType } from '../../workflow/runtime/type';
1414
import { type StoreSecretValueType } from '../../../common/secret/type';
1515
import { jsonSchema2NodeInput } from '../jsonschema';
1616
import { getNanoid } from '../../../common/string/tools';
17+
import { PluginSourceEnum } from '../plugin/constants';
1718

1819
export const getMCPToolSetRuntimeNode = ({
1920
url,
2021
toolList,
2122
headerSecret,
2223
name,
23-
avatar
24+
avatar,
25+
toolId
2426
}: {
2527
url: string;
2628
toolList: McpToolConfigType[];
2729
headerSecret?: StoreSecretValueType;
2830
name?: string;
2931
avatar?: string;
32+
toolId: string;
3033
}): RuntimeNodeItemType => {
3134
return {
3235
nodeId: getNanoid(16),
3336
flowNodeType: FlowNodeTypeEnum.toolSet,
3437
avatar,
35-
intro: '',
36-
inputs: [
37-
{
38-
key: NodeInputKeyEnum.toolSetData,
39-
label: '',
40-
valueType: WorkflowIOValueTypeEnum.object,
41-
renderTypeList: [FlowNodeInputTypeEnum.hidden],
42-
value: {
43-
url,
44-
headerSecret,
45-
toolList
46-
}
38+
intro: 'MCP Tools',
39+
toolConfig: {
40+
mcpToolSet: {
41+
toolList,
42+
headerSecret,
43+
url,
44+
toolId
4745
}
48-
],
46+
},
47+
inputs: [],
4948
outputs: [],
5049
name: name || '',
5150
version: ''
@@ -54,34 +53,24 @@ export const getMCPToolSetRuntimeNode = ({
5453

5554
export const getMCPToolRuntimeNode = ({
5655
tool,
57-
url,
58-
headerSecret,
59-
avatar = 'core/app/type/mcpToolsFill'
56+
avatar = 'core/app/type/mcpToolsFill',
57+
parentId
6058
}: {
6159
tool: McpToolConfigType;
62-
url: string;
63-
headerSecret?: StoreSecretValueType;
6460
avatar?: string;
61+
parentId: string;
6562
}): RuntimeNodeItemType => {
6663
return {
6764
nodeId: getNanoid(16),
6865
flowNodeType: FlowNodeTypeEnum.tool,
6966
avatar,
7067
intro: tool.description,
71-
inputs: [
72-
{
73-
key: NodeInputKeyEnum.toolData,
74-
label: 'Tool Data',
75-
valueType: WorkflowIOValueTypeEnum.object,
76-
renderTypeList: [FlowNodeInputTypeEnum.hidden],
77-
value: {
78-
...tool,
79-
url,
80-
headerSecret
81-
}
82-
},
83-
...jsonSchema2NodeInput(tool.inputSchema)
84-
],
68+
toolConfig: {
69+
mcpTool: {
70+
toolId: `${PluginSourceEnum.mcp}-${parentId}/${tool.name}`
71+
}
72+
},
73+
inputs: [...jsonSchema2NodeInput(tool.inputSchema)],
8574
outputs: [
8675
{
8776
id: NodeOutputKeyEnum.rawResponse,
@@ -97,3 +86,12 @@ export const getMCPToolRuntimeNode = ({
9786
version: ''
9887
};
9988
};
89+
90+
/**
91+
* Get the parent id of the mcp toolset
92+
* mcp-123123/toolName ==> 123123
93+
* 123123/toolName ==> 123123
94+
* @param id mcp-parentId/name or parentId/name
95+
* @returns parentId
96+
*/
97+
export const getMCPParentId = (id: string) => id.split('-').pop()?.split('/')[0];

packages/global/core/app/plugin/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export enum PluginSourceEnum {
22
personal = 'personal', // this is a app.
33
systemTool = 'systemTool', // FastGPT-plugin tools, pure code.
44
commercial = 'commercial', // configured in Pro, with associatedPluginId. Specially, commercial-dalle3 is a systemTool
5+
mcp = 'mcp', // mcp
56
// @deprecated
67
community = 'community' // this is deprecated, will be replaced by systemTool
78
}

packages/global/core/app/plugin/utils.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { type StoreNodeItemType } from '../../workflow/type/node';
22
import { type FlowNodeInputItemType } from '../../workflow/type/io';
33
import { FlowNodeTypeEnum } from '../../workflow/node/constant';
4+
import { PluginSourceEnum } from './constants';
45

56
export const getPluginInputsFromStoreNodes = (nodes: StoreNodeItemType[]) => {
67
return nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput)?.inputs || [];
@@ -22,3 +23,41 @@ export const getPluginRunContent = ({
2223
});
2324
return JSON.stringify(pluginInputsWithValue);
2425
};
26+
27+
/**
28+
plugin id rule:
29+
- personal: ObjectId
30+
- commercial: commercial-ObjectId
31+
- systemtool: systemTool-id
32+
- mcp tool: mcp-parentId/toolName
33+
(deprecated) community: community-id
34+
*/
35+
export function splitCombinePluginId(id: string) {
36+
const splitRes = id.split('-');
37+
if (splitRes.length === 1) {
38+
// app id
39+
return {
40+
source: PluginSourceEnum.personal,
41+
pluginId: id
42+
};
43+
}
44+
45+
const [source, pluginId] = id.split('-') as [PluginSourceEnum, string | undefined];
46+
if (!source || !pluginId) throw new Error('pluginId not found');
47+
48+
// 兼容4.10.0 之前的插件
49+
if (source === 'community' || id === 'commercial-dalle3') {
50+
return {
51+
source: PluginSourceEnum.systemTool,
52+
pluginId: `${PluginSourceEnum.systemTool}-${pluginId}`
53+
};
54+
}
55+
56+
if (source === 'mcp') {
57+
return {
58+
source: PluginSourceEnum.mcp,
59+
pluginId
60+
};
61+
}
62+
return { source, pluginId: id };
63+
}

packages/global/core/workflow/type/node.d.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,28 @@ import type { AppDetailType, AppSchema, McpToolConfigType } from '../../app/type
2323
import type { ParentIdType } from 'common/parentFolder/type';
2424
import { AppTypeEnum } from '../../app/constants';
2525
import type { WorkflowInteractiveResponseType } from '../template/system/interactive/type';
26+
import type { StoreSecretValueType } from '../../../common/secret/type';
2627

2728
export type NodeToolConfigType = {
28-
mcpTool?: McpToolConfigType & {
29+
mcpToolSet?: {
30+
toolId: string; // ObjectId of the MCP App
2931
url: string;
32+
headerSecret?: StoreSecretValueType;
33+
toolList: McpToolConfigType[];
34+
};
35+
mcpTool?: {
36+
toolId: string;
3037
};
3138
systemTool?: {
3239
toolId: string;
3340
};
41+
systemToolSet?: {
42+
toolId: string;
43+
toolList: {
44+
name: string;
45+
description: string;
46+
}[];
47+
};
3448
};
3549

3650
export type FlowNodeCommonType = {

packages/global/core/workflow/utils.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
type ReferenceArrayValueType,
1919
type ReferenceItemValueType
2020
} from './type/io.d';
21+
import type { NodeToolConfigType } from './type/node';
2122
import { type StoreNodeItemType } from './type/node';
2223
import type {
2324
VariableItemType,
@@ -333,12 +334,14 @@ export const toolSetData2FlowNodeIO = ({
333334
}): {
334335
inputs: FlowNodeInputItemType[];
335336
outputs: FlowNodeOutputItemType[];
337+
toolConfig?: NodeToolConfigType;
336338
} => {
337339
const toolSetNode = nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.toolSet);
338340

339341
return {
340342
inputs: toolSetNode?.inputs || [],
341-
outputs: toolSetNode?.outputs || []
343+
outputs: toolSetNode?.outputs || [],
344+
toolConfig: toolSetNode?.toolConfig
342345
};
343346
};
344347

packages/service/core/app/mcp.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
22
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
33
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
4+
import type { AppSchema } from '@fastgpt/global/core/app/type';
45
import { type McpToolConfigType } from '@fastgpt/global/core/app/type';
56
import { addLog } from '../../common/system/log';
67
import { retryFn } from '@fastgpt/global/common/system/utils';
8+
import { PluginSourceEnum } from '@fastgpt/global/core/app/plugin/constants';
9+
import { MongoApp } from './schema';
10+
import type { McpToolDataType } from '@fastgpt/global/core/app/mcpTools/type';
711

812
export class MCPClient {
913
private client: Client;
@@ -128,3 +132,31 @@ export class MCPClient {
128132
}
129133
}
130134
}
135+
136+
export const getMCPChildren = async (app: AppSchema) => {
137+
const isNewMcp = !!app.modules[0].toolConfig?.mcpToolSet;
138+
const id = String(app._id);
139+
if (isNewMcp) {
140+
return (
141+
app.modules[0].toolConfig?.mcpToolSet?.toolList.map((item) => ({
142+
...item,
143+
id: `${PluginSourceEnum.mcp}-${id}/${item.name}`,
144+
avatar: app.avatar
145+
})) ?? []
146+
);
147+
} else {
148+
const children = await MongoApp.find({
149+
parentId: id
150+
});
151+
152+
return children.map((item) => {
153+
const node = item.modules[0];
154+
const toolData: McpToolDataType = node.inputs[0].value;
155+
return {
156+
avatar: app.avatar,
157+
id: String(item._id),
158+
...toolData
159+
};
160+
});
161+
}
162+
};

0 commit comments

Comments
 (0)