mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-20 19:24:29 +00:00
support mcp client streamable http (#4650)
* support mcp streamable http * fix * fix * remove deps
This commit is contained in:
@@ -3,19 +3,19 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apidevtools/swagger-parser": "^10.1.0",
|
"@apidevtools/swagger-parser": "^10.1.0",
|
||||||
|
"@bany/curl-to-json": "^1.2.8",
|
||||||
"axios": "^1.8.2",
|
"axios": "^1.8.2",
|
||||||
"cron-parser": "^4.9.0",
|
"cron-parser": "^4.9.0",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
"encoding": "^0.1.13",
|
"encoding": "^0.1.13",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"jschardet": "3.1.1",
|
"jschardet": "3.1.1",
|
||||||
|
"json5": "^2.2.3",
|
||||||
"nanoid": "^5.1.3",
|
"nanoid": "^5.1.3",
|
||||||
"next": "14.2.26",
|
"next": "14.2.26",
|
||||||
"openai": "4.61.0",
|
"openai": "4.61.0",
|
||||||
"openapi-types": "^12.1.3",
|
"openapi-types": "^12.1.3",
|
||||||
"json5": "^2.2.3",
|
"timezones-list": "^3.0.2"
|
||||||
"timezones-list": "^3.0.2",
|
|
||||||
"@bany/curl-to-json": "^1.2.8"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
|
@@ -3,5 +3,5 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": "."
|
"baseUrl": "."
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.d.ts"]
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.d.ts", "../service/core/app/mcp.ts"]
|
||||||
}
|
}
|
||||||
|
112
packages/service/core/app/mcp.ts
Normal file
112
packages/service/core/app/mcp.ts
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
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 { ToolType } from '@fastgpt/global/core/app/type';
|
||||||
|
|
||||||
|
export class MCPClient {
|
||||||
|
private client: Client | null = null;
|
||||||
|
private url: string;
|
||||||
|
|
||||||
|
constructor(config: { url: string }) {
|
||||||
|
this.url = config.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getConnection(): Promise<Client> {
|
||||||
|
if (this.client) {
|
||||||
|
return this.client;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const client = new Client({
|
||||||
|
name: 'FastGPT-MCP-http-client',
|
||||||
|
version: '1.0.0'
|
||||||
|
});
|
||||||
|
const transport = new StreamableHTTPClientTransport(new URL(this.url));
|
||||||
|
await client.connect(transport);
|
||||||
|
this.client = client;
|
||||||
|
return client;
|
||||||
|
} catch (error) {
|
||||||
|
const client = new Client({
|
||||||
|
name: 'FastGPT-MCP-sse-client',
|
||||||
|
version: '1.0.0'
|
||||||
|
});
|
||||||
|
const sseTransport = new SSEClientTransport(new URL(this.url));
|
||||||
|
await client.connect(sseTransport);
|
||||||
|
this.client = client;
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 内部方法:关闭连接
|
||||||
|
private async closeConnection() {
|
||||||
|
if (this.client) {
|
||||||
|
try {
|
||||||
|
await this.client.close();
|
||||||
|
this.client = null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to close MCP client:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get available tools list
|
||||||
|
* @returns List of tools
|
||||||
|
*/
|
||||||
|
public async getTools(): Promise<ToolType[]> {
|
||||||
|
try {
|
||||||
|
const client = await this.getConnection();
|
||||||
|
const response = await client.listTools();
|
||||||
|
|
||||||
|
const tools = (response.tools || []).map((tool: any) => ({
|
||||||
|
name: tool.name,
|
||||||
|
description: tool.description || '',
|
||||||
|
inputSchema: tool.inputSchema || {
|
||||||
|
type: 'object',
|
||||||
|
properties: {}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
return tools;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to get MCP tools:', error);
|
||||||
|
return Promise.reject(error);
|
||||||
|
} finally {
|
||||||
|
await this.closeConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call tool
|
||||||
|
* @param toolName Tool name
|
||||||
|
* @param params Parameters
|
||||||
|
* @returns Tool execution result
|
||||||
|
*/
|
||||||
|
public async toolCall(toolName: string, params: Record<string, any>): Promise<any> {
|
||||||
|
try {
|
||||||
|
const client = await this.getConnection();
|
||||||
|
console.log(`Call tool: ${toolName}`, params);
|
||||||
|
|
||||||
|
const result = await client.callTool({
|
||||||
|
name: toolName,
|
||||||
|
arguments: params
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to call tool ${toolName}:`, error);
|
||||||
|
return Promise.reject(error);
|
||||||
|
} finally {
|
||||||
|
await this.closeConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create MCP client
|
||||||
|
* @param config Client configuration, containing url
|
||||||
|
* @returns MCPClient instance
|
||||||
|
*/
|
||||||
|
export default function getMCPClient(config: { url: string }): MCPClient {
|
||||||
|
return new MCPClient(config);
|
||||||
|
}
|
@@ -2,10 +2,9 @@ import {
|
|||||||
DispatchNodeResultType,
|
DispatchNodeResultType,
|
||||||
ModuleDispatchProps
|
ModuleDispatchProps
|
||||||
} from '@fastgpt/global/core/workflow/runtime/type';
|
} from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
||||||
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||||
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
|
import getMCPClient from '../../../app/mcp';
|
||||||
|
|
||||||
type RunToolProps = ModuleDispatchProps<{
|
type RunToolProps = ModuleDispatchProps<{
|
||||||
toolData: {
|
toolData: {
|
||||||
@@ -27,34 +26,21 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
|
|||||||
const { toolData, ...restParams } = params;
|
const { toolData, ...restParams } = params;
|
||||||
const { name: toolName, url } = toolData;
|
const { name: toolName, url } = toolData;
|
||||||
|
|
||||||
const client = new Client({
|
const mcpClient = getMCPClient({ url });
|
||||||
name: 'FastGPT-MCP-client',
|
|
||||||
version: '1.0.0'
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await (async () => {
|
try {
|
||||||
try {
|
const result = await mcpClient.toolCall(toolName, restParams);
|
||||||
const transport = new SSEClientTransport(new URL(url));
|
|
||||||
await client.connect(transport);
|
|
||||||
|
|
||||||
return await client.callTool({
|
return {
|
||||||
name: toolName,
|
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||||
arguments: restParams
|
toolRes: result,
|
||||||
});
|
moduleLogo: avatar
|
||||||
} catch (error) {
|
},
|
||||||
console.error('Error running MCP tool:', error);
|
[DispatchNodeResponseKeyEnum.toolResponses]: result,
|
||||||
return Promise.reject(error);
|
[NodeOutputKeyEnum.rawResponse]: result
|
||||||
} finally {
|
};
|
||||||
await client.close();
|
} catch (error) {
|
||||||
}
|
console.error('Error running MCP tool:', error);
|
||||||
})();
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
return {
|
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
|
||||||
toolRes: result,
|
|
||||||
moduleLogo: avatar
|
|
||||||
},
|
|
||||||
[DispatchNodeResponseKeyEnum.toolResponses]: result,
|
|
||||||
[NodeOutputKeyEnum.rawResponse]: result
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fastgpt/global": "workspace:*",
|
"@fastgpt/global": "workspace:*",
|
||||||
"@modelcontextprotocol/sdk": "^1.9.0",
|
"@modelcontextprotocol/sdk": "^1.10.0",
|
||||||
"@node-rs/jieba": "2.0.1",
|
"@node-rs/jieba": "2.0.1",
|
||||||
"@xmldom/xmldom": "^0.8.10",
|
"@xmldom/xmldom": "^0.8.10",
|
||||||
"@zilliz/milvus2-sdk-node": "2.4.2",
|
"@zilliz/milvus2-sdk-node": "2.4.2",
|
||||||
|
64
pnpm-lock.yaml
generated
64
pnpm-lock.yaml
generated
@@ -82,7 +82,7 @@ importers:
|
|||||||
version: 5.1.3
|
version: 5.1.3
|
||||||
next:
|
next:
|
||||||
specifier: 14.2.26
|
specifier: 14.2.26
|
||||||
version: 14.2.26(@babel/core@7.26.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)
|
version: 14.2.26(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)
|
||||||
openai:
|
openai:
|
||||||
specifier: 4.61.0
|
specifier: 4.61.0
|
||||||
version: 4.61.0(encoding@0.1.13)(zod@3.24.2)
|
version: 4.61.0(encoding@0.1.13)(zod@3.24.2)
|
||||||
@@ -164,8 +164,8 @@ importers:
|
|||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../global
|
version: link:../global
|
||||||
'@modelcontextprotocol/sdk':
|
'@modelcontextprotocol/sdk':
|
||||||
specifier: ^1.9.0
|
specifier: ^1.10.0
|
||||||
version: 1.9.0
|
version: 1.10.2
|
||||||
'@node-rs/jieba':
|
'@node-rs/jieba':
|
||||||
specifier: 2.0.1
|
specifier: 2.0.1
|
||||||
version: 2.0.1
|
version: 2.0.1
|
||||||
@@ -490,8 +490,8 @@ importers:
|
|||||||
specifier: ^3.0.6
|
specifier: ^3.0.6
|
||||||
version: 3.0.6
|
version: 3.0.6
|
||||||
'@modelcontextprotocol/sdk':
|
'@modelcontextprotocol/sdk':
|
||||||
specifier: ^1.9.0
|
specifier: ^1.10.0
|
||||||
version: 1.9.0
|
version: 1.10.2
|
||||||
'@node-rs/jieba':
|
'@node-rs/jieba':
|
||||||
specifier: 2.0.1
|
specifier: 2.0.1
|
||||||
version: 2.0.1
|
version: 2.0.1
|
||||||
@@ -2355,6 +2355,10 @@ packages:
|
|||||||
'@mixmark-io/domino@2.2.0':
|
'@mixmark-io/domino@2.2.0':
|
||||||
resolution: {integrity: sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==}
|
resolution: {integrity: sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==}
|
||||||
|
|
||||||
|
'@modelcontextprotocol/sdk@1.10.2':
|
||||||
|
resolution: {integrity: sha512-rb6AMp2DR4SN+kc6L1ta2NCpApyA9WYNx3CrTSZvGxq9wH71bRur+zRqPfg0vQ9mjywR7qZdX2RGHOPq3ss+tA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
'@modelcontextprotocol/sdk@1.9.0':
|
'@modelcontextprotocol/sdk@1.9.0':
|
||||||
resolution: {integrity: sha512-Jq2EUCQpe0iyO5FGpzVYDNFR6oR53AIrwph9yWl7uSc7IWUMsrmpmSaTGra5hQNunXpM+9oit85p924jWuHzUA==}
|
resolution: {integrity: sha512-Jq2EUCQpe0iyO5FGpzVYDNFR6oR53AIrwph9yWl7uSc7IWUMsrmpmSaTGra5hQNunXpM+9oit85p924jWuHzUA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -11866,6 +11870,21 @@ snapshots:
|
|||||||
|
|
||||||
'@mixmark-io/domino@2.2.0': {}
|
'@mixmark-io/domino@2.2.0': {}
|
||||||
|
|
||||||
|
'@modelcontextprotocol/sdk@1.10.2':
|
||||||
|
dependencies:
|
||||||
|
content-type: 1.0.5
|
||||||
|
cors: 2.8.5
|
||||||
|
cross-spawn: 7.0.6
|
||||||
|
eventsource: 3.0.6
|
||||||
|
express: 5.1.0
|
||||||
|
express-rate-limit: 7.5.0(express@5.1.0)
|
||||||
|
pkce-challenge: 5.0.0
|
||||||
|
raw-body: 3.0.0
|
||||||
|
zod: 3.24.2
|
||||||
|
zod-to-json-schema: 3.24.5(zod@3.24.2)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
'@modelcontextprotocol/sdk@1.9.0':
|
'@modelcontextprotocol/sdk@1.9.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
content-type: 1.0.5
|
content-type: 1.0.5
|
||||||
@@ -13662,7 +13681,7 @@ snapshots:
|
|||||||
|
|
||||||
axios@1.8.3:
|
axios@1.8.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects: 1.15.9(debug@4.4.0)
|
follow-redirects: 1.15.9
|
||||||
form-data: 4.0.2
|
form-data: 4.0.2
|
||||||
proxy-from-env: 1.1.0
|
proxy-from-env: 1.1.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -15624,6 +15643,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
follow-redirects@1.15.9: {}
|
||||||
|
|
||||||
follow-redirects@1.15.9(debug@4.4.0):
|
follow-redirects@1.15.9(debug@4.4.0):
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
debug: 4.4.0(supports-color@5.5.0)
|
debug: 4.4.0(supports-color@5.5.0)
|
||||||
@@ -18114,6 +18135,32 @@ snapshots:
|
|||||||
- '@babel/core'
|
- '@babel/core'
|
||||||
- babel-plugin-macros
|
- babel-plugin-macros
|
||||||
|
|
||||||
|
next@14.2.26(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1):
|
||||||
|
dependencies:
|
||||||
|
'@next/env': 14.2.26
|
||||||
|
'@swc/helpers': 0.5.5
|
||||||
|
busboy: 1.6.0
|
||||||
|
caniuse-lite: 1.0.30001704
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
postcss: 8.4.31
|
||||||
|
react: 18.3.1
|
||||||
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
|
styled-jsx: 5.1.1(react@18.3.1)
|
||||||
|
optionalDependencies:
|
||||||
|
'@next/swc-darwin-arm64': 14.2.26
|
||||||
|
'@next/swc-darwin-x64': 14.2.26
|
||||||
|
'@next/swc-linux-arm64-gnu': 14.2.26
|
||||||
|
'@next/swc-linux-arm64-musl': 14.2.26
|
||||||
|
'@next/swc-linux-x64-gnu': 14.2.26
|
||||||
|
'@next/swc-linux-x64-musl': 14.2.26
|
||||||
|
'@next/swc-win32-arm64-msvc': 14.2.26
|
||||||
|
'@next/swc-win32-ia32-msvc': 14.2.26
|
||||||
|
'@next/swc-win32-x64-msvc': 14.2.26
|
||||||
|
sass: 1.85.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@babel/core'
|
||||||
|
- babel-plugin-macros
|
||||||
|
|
||||||
nextjs-cors@2.2.0(next@14.2.26(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)):
|
nextjs-cors@2.2.0(next@14.2.26(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)):
|
||||||
dependencies:
|
dependencies:
|
||||||
cors: 2.8.5
|
cors: 2.8.5
|
||||||
@@ -19797,6 +19844,11 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@babel/core': 7.26.10
|
'@babel/core': 7.26.10
|
||||||
|
|
||||||
|
styled-jsx@5.1.1(react@18.3.1):
|
||||||
|
dependencies:
|
||||||
|
client-only: 0.0.1
|
||||||
|
react: 18.3.1
|
||||||
|
|
||||||
stylis@4.2.0: {}
|
stylis@4.2.0: {}
|
||||||
|
|
||||||
stylis@4.3.6: {}
|
stylis@4.3.6: {}
|
||||||
|
@@ -23,7 +23,7 @@
|
|||||||
"@fastgpt/templates": "workspace:*",
|
"@fastgpt/templates": "workspace:*",
|
||||||
"@fastgpt/web": "workspace:*",
|
"@fastgpt/web": "workspace:*",
|
||||||
"@fortaine/fetch-event-source": "^3.0.6",
|
"@fortaine/fetch-event-source": "^3.0.6",
|
||||||
"@modelcontextprotocol/sdk": "^1.9.0",
|
"@modelcontextprotocol/sdk": "^1.10.0",
|
||||||
"@node-rs/jieba": "2.0.1",
|
"@node-rs/jieba": "2.0.1",
|
||||||
"@tanstack/react-query": "^4.24.10",
|
"@tanstack/react-query": "^4.24.10",
|
||||||
"ahooks": "^3.7.11",
|
"ahooks": "^3.7.11",
|
||||||
|
@@ -15,7 +15,7 @@ import MyNumberInput from '@fastgpt/web/components/common/Input/NumberInput';
|
|||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
import Markdown from '@/components/Markdown';
|
import Markdown from '@/components/Markdown';
|
||||||
import { postRunMCPTools } from '@/web/core/app/api/plugin';
|
import { postRunMCPTool } from '@/web/core/app/api/plugin';
|
||||||
|
|
||||||
const JsonEditor = dynamic(() => import('@fastgpt/web/components/common/Textarea/JsonEditor'));
|
const JsonEditor = dynamic(() => import('@fastgpt/web/components/common/Textarea/JsonEditor'));
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ const ChatTest = ({ currentTool, url }: { currentTool: ToolType | null; url: str
|
|||||||
const { runAsync: runTool, loading: isRunning } = useRequest2(
|
const { runAsync: runTool, loading: isRunning } = useRequest2(
|
||||||
async (data: Record<string, any>) => {
|
async (data: Record<string, any>) => {
|
||||||
if (!currentTool) return;
|
if (!currentTool) return;
|
||||||
return await postRunMCPTools({
|
return await postRunMCPTool({
|
||||||
params: data,
|
params: data,
|
||||||
url,
|
url,
|
||||||
toolName: currentTool.name
|
toolName: currentTool.name
|
||||||
|
@@ -4,7 +4,6 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
|
|||||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
import { getMCPTools } from '@/web/core/app/api/plugin';
|
|
||||||
import { AppContext } from '../context';
|
import { AppContext } from '../context';
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
|
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
|
||||||
@@ -12,7 +11,8 @@ import { ToolType } from '@fastgpt/global/core/app/type';
|
|||||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||||
import { getMCPToolsBody } from '@/pages/api/core/app/mcpTools/getMCPTools';
|
import type { getMCPToolsBody } from '@/pages/api/support/mcp/client/getTools';
|
||||||
|
import { getMCPTools } from '@/web/core/app/api/plugin';
|
||||||
|
|
||||||
const EditForm = ({
|
const EditForm = ({
|
||||||
url,
|
url,
|
||||||
|
@@ -26,7 +26,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { AppListContext } from './context';
|
import { AppListContext } from './context';
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import { ToolType } from '@fastgpt/global/core/app/type';
|
import { ToolType } from '@fastgpt/global/core/app/type';
|
||||||
import { getMCPToolsBody } from '@/pages/api/core/app/mcpTools/getMCPTools';
|
import type { getMCPToolsBody } from '@/pages/api/support/mcp/client/getTools';
|
||||||
|
|
||||||
export type MCPToolSetData = {
|
export type MCPToolSetData = {
|
||||||
url: string;
|
url: string;
|
||||||
@@ -81,7 +81,7 @@ const MCPToolsEditModal = ({ onClose }: { onClose: () => void }) => {
|
|||||||
const { runAsync: runGetMCPTools, loading: isGettingTools } = useRequest2(
|
const { runAsync: runGetMCPTools, loading: isGettingTools } = useRequest2(
|
||||||
(data: getMCPToolsBody) => getMCPTools(data),
|
(data: getMCPToolsBody) => getMCPTools(data),
|
||||||
{
|
{
|
||||||
onSuccess: (res) => {
|
onSuccess: (res: ToolType[]) => {
|
||||||
setValue('mcpData.toolList', res);
|
setValue('mcpData.toolList', res);
|
||||||
},
|
},
|
||||||
errorToast: t('app:MCP_tools_parse_failed')
|
errorToast: t('app:MCP_tools_parse_failed')
|
||||||
|
@@ -11,6 +11,7 @@ import {
|
|||||||
getMCPToolRuntimeNode,
|
getMCPToolRuntimeNode,
|
||||||
getMCPToolSetRuntimeNode
|
getMCPToolSetRuntimeNode
|
||||||
} from '@fastgpt/global/core/app/mcpTools/utils';
|
} from '@fastgpt/global/core/app/mcpTools/utils';
|
||||||
|
import { pushTrack } from '@fastgpt/service/common/middle/tracks/utils';
|
||||||
|
|
||||||
export type createMCPToolsQuery = {};
|
export type createMCPToolsQuery = {};
|
||||||
|
|
||||||
@@ -61,6 +62,13 @@ async function handler(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pushTrack.createApp({
|
||||||
|
type: AppTypeEnum.toolSet,
|
||||||
|
uid: userId,
|
||||||
|
teamId,
|
||||||
|
tmbId
|
||||||
|
});
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,43 +0,0 @@
|
|||||||
import { NextAPI } from '@/service/middleware/entry';
|
|
||||||
import { ToolType } from '@fastgpt/global/core/app/type';
|
|
||||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
|
||||||
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
||||||
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
||||||
|
|
||||||
export type getMCPToolsQuery = {};
|
|
||||||
|
|
||||||
export type getMCPToolsBody = { url: string };
|
|
||||||
|
|
||||||
export type getMCPToolsResponse = ToolType[];
|
|
||||||
|
|
||||||
async function handler(
|
|
||||||
req: ApiRequestProps<getMCPToolsBody, getMCPToolsQuery>,
|
|
||||||
res: ApiResponseType<getMCPToolsResponse[]>
|
|
||||||
): Promise<getMCPToolsResponse> {
|
|
||||||
const { url } = req.body;
|
|
||||||
|
|
||||||
const client = new Client({
|
|
||||||
name: 'FastGPT-MCP-client',
|
|
||||||
version: '1.0.0'
|
|
||||||
});
|
|
||||||
|
|
||||||
const tools = await (async () => {
|
|
||||||
try {
|
|
||||||
const transport = new SSEClientTransport(new URL(url));
|
|
||||||
await client.connect(transport);
|
|
||||||
|
|
||||||
const response = await client.listTools();
|
|
||||||
|
|
||||||
return response.tools || [];
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching MCP tools:', error);
|
|
||||||
return Promise.reject(error);
|
|
||||||
} finally {
|
|
||||||
await client.close();
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
return tools as ToolType[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NextAPI(handler);
|
|
@@ -1,45 +0,0 @@
|
|||||||
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
||||||
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
||||||
import { NextAPI } from '@/service/middleware/entry';
|
|
||||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
|
||||||
|
|
||||||
export type RunToolTestQuery = {};
|
|
||||||
export type RunToolTestBody = {
|
|
||||||
params: Record<string, any>;
|
|
||||||
url: string;
|
|
||||||
toolName: string;
|
|
||||||
};
|
|
||||||
export type RunToolTestResponse = any;
|
|
||||||
|
|
||||||
async function handler(
|
|
||||||
req: ApiRequestProps<RunToolTestBody, RunToolTestQuery>,
|
|
||||||
res: ApiResponseType<RunToolTestResponse>
|
|
||||||
): Promise<RunToolTestResponse> {
|
|
||||||
const { params, url, toolName } = req.body;
|
|
||||||
|
|
||||||
const client = new Client({
|
|
||||||
name: 'FastGPT-MCP-client',
|
|
||||||
version: '1.0.0'
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await (async () => {
|
|
||||||
try {
|
|
||||||
const transport = new SSEClientTransport(new URL(url));
|
|
||||||
await client.connect(transport);
|
|
||||||
|
|
||||||
return await client.callTool({
|
|
||||||
name: toolName,
|
|
||||||
arguments: params
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error running MCP tool test:', error);
|
|
||||||
return Promise.reject(error);
|
|
||||||
} finally {
|
|
||||||
await client.close();
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NextAPI(handler);
|
|
28
projects/app/src/pages/api/support/mcp/client/getTools.ts
Normal file
28
projects/app/src/pages/api/support/mcp/client/getTools.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { NextAPI } from '@/service/middleware/entry';
|
||||||
|
import { ToolType } from '@fastgpt/global/core/app/type';
|
||||||
|
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||||
|
import getMCPClient from '@fastgpt/service/core/app/mcp';
|
||||||
|
|
||||||
|
export type getMCPToolsQuery = {};
|
||||||
|
|
||||||
|
export type getMCPToolsBody = { url: string };
|
||||||
|
|
||||||
|
export type getMCPToolsResponse = ToolType[];
|
||||||
|
|
||||||
|
async function handler(
|
||||||
|
req: ApiRequestProps<getMCPToolsBody, getMCPToolsQuery>,
|
||||||
|
res: ApiResponseType<getMCPToolsResponse[]>
|
||||||
|
): Promise<getMCPToolsResponse> {
|
||||||
|
const { url } = req.body;
|
||||||
|
|
||||||
|
const mcpClient = getMCPClient({ url });
|
||||||
|
|
||||||
|
try {
|
||||||
|
const tools = await mcpClient.getTools();
|
||||||
|
return tools;
|
||||||
|
} catch (error) {
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NextAPI(handler);
|
31
projects/app/src/pages/api/support/mcp/client/runTool.ts
Normal file
31
projects/app/src/pages/api/support/mcp/client/runTool.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { NextAPI } from '@/service/middleware/entry';
|
||||||
|
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||||
|
import getMCPClient from '@fastgpt/service/core/app/mcp';
|
||||||
|
|
||||||
|
export type RunMCPToolQuery = {};
|
||||||
|
|
||||||
|
export type RunMCPToolBody = {
|
||||||
|
url: string;
|
||||||
|
toolName: string;
|
||||||
|
params: Record<string, any>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RunMCPToolResponse = any;
|
||||||
|
|
||||||
|
async function handler(
|
||||||
|
req: ApiRequestProps<RunMCPToolBody, RunMCPToolQuery>,
|
||||||
|
res: ApiResponseType<RunMCPToolResponse>
|
||||||
|
): Promise<RunMCPToolResponse> {
|
||||||
|
const { url, toolName, params } = req.body;
|
||||||
|
|
||||||
|
const mcpClient = getMCPClient({ url });
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await mcpClient.toolCall(toolName, params);
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NextAPI(handler);
|
@@ -19,11 +19,11 @@ import type { GetSystemPluginTemplatesBody } from '@/pages/api/core/app/plugin/g
|
|||||||
import type { PluginGroupSchemaType } from '@fastgpt/service/core/app/plugin/type';
|
import type { PluginGroupSchemaType } from '@fastgpt/service/core/app/plugin/type';
|
||||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
import { defaultGroup } from '@fastgpt/web/core/workflow/constants';
|
import { defaultGroup } from '@fastgpt/web/core/workflow/constants';
|
||||||
import { createMCPToolsBody } from '@/pages/api/core/app/mcpTools/create';
|
import type { createMCPToolsBody } from '@/pages/api/core/app/mcpTools/create';
|
||||||
import { ToolType } from '@fastgpt/global/core/app/type';
|
import { ToolType } from '@fastgpt/global/core/app/type';
|
||||||
import { getMCPToolsBody } from '@/pages/api/core/app/mcpTools/getMCPTools';
|
import type { updateMCPToolsBody } from '@/pages/api/core/app/mcpTools/update';
|
||||||
import { RunToolTestBody } from '@/pages/api/core/app/mcpTools/runTest';
|
import type { RunMCPToolBody } from '@/pages/api/support/mcp/client/runTool';
|
||||||
import { updateMCPToolsBody } from '@/pages/api/core/app/mcpTools/update';
|
import type { getMCPToolsBody } from '@/pages/api/support/mcp/client/getTools';
|
||||||
|
|
||||||
/* ============ team plugin ============== */
|
/* ============ team plugin ============== */
|
||||||
export const getTeamPlugTemplates = (data?: ListAppBody) =>
|
export const getTeamPlugTemplates = (data?: ListAppBody) =>
|
||||||
@@ -72,16 +72,16 @@ export const getPreviewPluginNode = (data: GetPreviewNodeQuery) =>
|
|||||||
GET<FlowNodeTemplateType>('/core/app/plugin/getPreviewNode', data);
|
GET<FlowNodeTemplateType>('/core/app/plugin/getPreviewNode', data);
|
||||||
|
|
||||||
/* ============ mcp tools ============== */
|
/* ============ mcp tools ============== */
|
||||||
export const getMCPTools = (data: getMCPToolsBody) =>
|
|
||||||
POST<ToolType[]>('/core/app/mcpTools/getMCPTools', data);
|
|
||||||
|
|
||||||
export const postCreateMCPTools = (data: createMCPToolsBody) =>
|
export const postCreateMCPTools = (data: createMCPToolsBody) =>
|
||||||
POST('/core/app/mcpTools/create', data);
|
POST('/core/app/mcpTools/create', data);
|
||||||
|
|
||||||
export const postUpdateMCPTools = (data: updateMCPToolsBody) =>
|
export const postUpdateMCPTools = (data: updateMCPToolsBody) =>
|
||||||
POST('/core/app/mcpTools/update', data);
|
POST('/core/app/mcpTools/update', data);
|
||||||
|
|
||||||
export const postRunMCPTools = (data: RunToolTestBody) => POST('/core/app/mcpTools/runTest', data);
|
export const getMCPTools = (data: getMCPToolsBody) =>
|
||||||
|
POST<ToolType[]>('/support/mcp/client/getTools', data);
|
||||||
|
|
||||||
|
export const postRunMCPTool = (data: RunMCPToolBody) => POST('/support/mcp/client/runTool', data);
|
||||||
|
|
||||||
/* ============ http plugin ============== */
|
/* ============ http plugin ============== */
|
||||||
export const postCreateHttpPlugin = (data: createHttpPluginBody) =>
|
export const postCreateHttpPlugin = (data: createHttpPluginBody) =>
|
||||||
|
Reference in New Issue
Block a user