Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 31 additions & 33 deletions packages/global/core/app/mcpTools/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,38 +14,37 @@ import { type RuntimeNodeItemType } from '../../workflow/runtime/type';
import { type StoreSecretValueType } from '../../../common/secret/type';
import { jsonSchema2NodeInput } from '../jsonschema';
import { getNanoid } from '../../../common/string/tools';
import { PluginSourceEnum } from '../plugin/constants';

export const getMCPToolSetRuntimeNode = ({
url,
toolList,
headerSecret,
name,
avatar
avatar,
toolId
}: {
url: string;
toolList: McpToolConfigType[];
headerSecret?: StoreSecretValueType;
name?: string;
avatar?: string;
toolId: string;
}): RuntimeNodeItemType => {
return {
nodeId: getNanoid(16),
flowNodeType: FlowNodeTypeEnum.toolSet,
avatar,
intro: '',
inputs: [
{
key: NodeInputKeyEnum.toolSetData,
label: '',
valueType: WorkflowIOValueTypeEnum.object,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
value: {
url,
headerSecret,
toolList
}
intro: 'MCP Tools',
toolConfig: {
mcpToolSet: {
toolList,
headerSecret,
url,
toolId
}
],
},
inputs: [],
outputs: [],
name: name || '',
version: ''
Expand All @@ -54,34 +53,24 @@ export const getMCPToolSetRuntimeNode = ({

export const getMCPToolRuntimeNode = ({
tool,
url,
headerSecret,
avatar = 'core/app/type/mcpToolsFill'
avatar = 'core/app/type/mcpToolsFill',
parentId
}: {
tool: McpToolConfigType;
url: string;
headerSecret?: StoreSecretValueType;
avatar?: string;
parentId: string;
}): RuntimeNodeItemType => {
return {
nodeId: getNanoid(16),
flowNodeType: FlowNodeTypeEnum.tool,
avatar,
intro: tool.description,
inputs: [
{
key: NodeInputKeyEnum.toolData,
label: 'Tool Data',
valueType: WorkflowIOValueTypeEnum.object,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
value: {
...tool,
url,
headerSecret
}
},
...jsonSchema2NodeInput(tool.inputSchema)
],
toolConfig: {
mcpTool: {
toolId: `${PluginSourceEnum.mcp}-${parentId}/${tool.name}`
}
},
inputs: [...jsonSchema2NodeInput(tool.inputSchema)],
outputs: [
{
id: NodeOutputKeyEnum.rawResponse,
Expand All @@ -97,3 +86,12 @@ export const getMCPToolRuntimeNode = ({
version: ''
};
};

/**
* Get the parent id of the mcp toolset
* mcp-123123/toolName ==> 123123
* 123123/toolName ==> 123123
* @param id mcp-parentId/name or parentId/name
* @returns parentId
*/
export const getMCPParentId = (id: string) => id.split('-').pop()?.split('/')[0];
1 change: 1 addition & 0 deletions packages/global/core/app/plugin/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export enum PluginSourceEnum {
personal = 'personal', // this is a app.
systemTool = 'systemTool', // FastGPT-plugin tools, pure code.
commercial = 'commercial', // configured in Pro, with associatedPluginId. Specially, commercial-dalle3 is a systemTool
mcp = 'mcp', // mcp
// @deprecated
community = 'community' // this is deprecated, will be replaced by systemTool
}
39 changes: 39 additions & 0 deletions packages/global/core/app/plugin/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { type StoreNodeItemType } from '../../workflow/type/node';
import { type FlowNodeInputItemType } from '../../workflow/type/io';
import { FlowNodeTypeEnum } from '../../workflow/node/constant';
import { PluginSourceEnum } from './constants';

export const getPluginInputsFromStoreNodes = (nodes: StoreNodeItemType[]) => {
return nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput)?.inputs || [];
Expand All @@ -22,3 +23,41 @@ export const getPluginRunContent = ({
});
return JSON.stringify(pluginInputsWithValue);
};

/**
plugin id rule:
- personal: ObjectId
- commercial: commercial-ObjectId
- systemtool: systemTool-id
- mcp tool: mcp-parentId/toolName
(deprecated) community: community-id
*/
export function splitCombinePluginId(id: string) {
const splitRes = id.split('-');
if (splitRes.length === 1) {
// app id
return {
source: PluginSourceEnum.personal,
pluginId: id
};
}

const [source, pluginId] = id.split('-') as [PluginSourceEnum, string | undefined];
if (!source || !pluginId) throw new Error('pluginId not found');

// 兼容4.10.0 之前的插件
if (source === 'community' || id === 'commercial-dalle3') {
return {
source: PluginSourceEnum.systemTool,
pluginId: `${PluginSourceEnum.systemTool}-${pluginId}`
};
}

if (source === 'mcp') {
return {
source: PluginSourceEnum.mcp,
pluginId
};
}
return { source, pluginId: id };
}
16 changes: 15 additions & 1 deletion packages/global/core/workflow/type/node.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,28 @@ import type { AppDetailType, AppSchema, McpToolConfigType } from '../../app/type
import type { ParentIdType } from 'common/parentFolder/type';
import { AppTypeEnum } from '../../app/constants';
import type { WorkflowInteractiveResponseType } from '../template/system/interactive/type';
import type { StoreSecretValueType } from '../../../common/secret/type';

export type NodeToolConfigType = {
mcpTool?: McpToolConfigType & {
mcpToolSet?: {
toolId: string; // ObjectId of the MCP App
url: string;
headerSecret?: StoreSecretValueType;
toolList: McpToolConfigType[];
};
mcpTool?: {
toolId: string;
};
systemTool?: {
toolId: string;
};
systemToolSet?: {
toolId: string;
toolList: {
name: string;
description: string;
}[];
};
};

export type FlowNodeCommonType = {
Expand Down
5 changes: 4 additions & 1 deletion packages/global/core/workflow/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
type ReferenceArrayValueType,
type ReferenceItemValueType
} from './type/io.d';
import type { NodeToolConfigType } from './type/node';
import { type StoreNodeItemType } from './type/node';
import type {
VariableItemType,
Expand Down Expand Up @@ -333,12 +334,14 @@ export const toolSetData2FlowNodeIO = ({
}): {
inputs: FlowNodeInputItemType[];
outputs: FlowNodeOutputItemType[];
toolConfig?: NodeToolConfigType;
} => {
const toolSetNode = nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.toolSet);

return {
inputs: toolSetNode?.inputs || [],
outputs: toolSetNode?.outputs || []
outputs: toolSetNode?.outputs || [],
toolConfig: toolSetNode?.toolConfig
};
};

Expand Down
32 changes: 32 additions & 0 deletions packages/service/core/app/mcp.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
import type { AppSchema } from '@fastgpt/global/core/app/type';
import { type McpToolConfigType } from '@fastgpt/global/core/app/type';
import { addLog } from '../../common/system/log';
import { retryFn } from '@fastgpt/global/common/system/utils';
import { PluginSourceEnum } from '@fastgpt/global/core/app/plugin/constants';
import { MongoApp } from './schema';
import type { McpToolDataType } from '@fastgpt/global/core/app/mcpTools/type';

export class MCPClient {
private client: Client;
Expand Down Expand Up @@ -128,3 +132,31 @@ export class MCPClient {
}
}
}

export const getMCPChildren = async (app: AppSchema) => {
const isNewMcp = !!app.modules[0].toolConfig?.mcpToolSet;
const id = String(app._id);
if (isNewMcp) {
return (
app.modules[0].toolConfig?.mcpToolSet?.toolList.map((item) => ({
...item,
id: `${PluginSourceEnum.mcp}-${id}/${item.name}`,
avatar: app.avatar
})) ?? []
);
} else {
const children = await MongoApp.find({
parentId: id
});

return children.map((item) => {
const node = item.modules[0];
const toolData: McpToolDataType = node.inputs[0].value;
return {
avatar: app.avatar,
id: String(item._id),
...toolData
};
});
}
};
Loading
Loading