mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-22 12:20:34 +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",
|
||||
"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",
|
||||
|
@@ -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"]
|
||||
}
|
||||
|
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,
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
@@ -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",
|
||||
|
Reference in New Issue
Block a user