support mcp client streamable http (#4650)

* support mcp streamable http

* fix

* fix

* remove deps
This commit is contained in:
heheer
2025-04-24 23:04:54 +08:00
committed by GitHub
parent 5c93545016
commit 2a54be4d91
16 changed files with 273 additions and 144 deletions

View File

@@ -3,19 +3,19 @@
"version": "1.0.0",
"dependencies": {
"@apidevtools/swagger-parser": "^10.1.0",
"@bany/curl-to-json": "^1.2.8",
"axios": "^1.8.2",
"cron-parser": "^4.9.0",
"dayjs": "^1.11.7",
"encoding": "^0.1.13",
"js-yaml": "^4.1.0",
"jschardet": "3.1.1",
"json5": "^2.2.3",
"nanoid": "^5.1.3",
"next": "14.2.26",
"openai": "4.61.0",
"openapi-types": "^12.1.3",
"json5": "^2.2.3",
"timezones-list": "^3.0.2",
"@bany/curl-to-json": "^1.2.8"
"timezones-list": "^3.0.2"
},
"devDependencies": {
"@types/js-yaml": "^4.0.9",

View File

@@ -3,5 +3,5 @@
"compilerOptions": {
"baseUrl": "."
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.d.ts"]
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.d.ts", "../service/core/app/mcp.ts"]
}

View 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);
}

View File

@@ -2,10 +2,9 @@ import {
DispatchNodeResultType,
ModuleDispatchProps
} 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 { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import getMCPClient from '../../../app/mcp';
type RunToolProps = ModuleDispatchProps<{
toolData: {
@@ -27,34 +26,21 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
const { toolData, ...restParams } = params;
const { name: toolName, url } = toolData;
const client = new Client({
name: 'FastGPT-MCP-client',
version: '1.0.0'
});
const mcpClient = getMCPClient({ url });
const result = await (async () => {
try {
const transport = new SSEClientTransport(new URL(url));
await client.connect(transport);
try {
const result = await mcpClient.toolCall(toolName, restParams);
return await client.callTool({
name: toolName,
arguments: restParams
});
} catch (error) {
console.error('Error running MCP tool:', error);
return Promise.reject(error);
} finally {
await client.close();
}
})();
return {
[DispatchNodeResponseKeyEnum.nodeResponse]: {
toolRes: result,
moduleLogo: avatar
},
[DispatchNodeResponseKeyEnum.toolResponses]: result,
[NodeOutputKeyEnum.rawResponse]: result
};
return {
[DispatchNodeResponseKeyEnum.nodeResponse]: {
toolRes: result,
moduleLogo: avatar
},
[DispatchNodeResponseKeyEnum.toolResponses]: result,
[NodeOutputKeyEnum.rawResponse]: result
};
} catch (error) {
console.error('Error running MCP tool:', error);
return Promise.reject(error);
}
};

View File

@@ -3,7 +3,7 @@
"version": "1.0.0",
"dependencies": {
"@fastgpt/global": "workspace:*",
"@modelcontextprotocol/sdk": "^1.9.0",
"@modelcontextprotocol/sdk": "^1.10.0",
"@node-rs/jieba": "2.0.1",
"@xmldom/xmldom": "^0.8.10",
"@zilliz/milvus2-sdk-node": "2.4.2",