1919import modelengine .fel .engine .flows .AiProcessFlow ;
2020import modelengine .fel .engine .operators .patterns .AbstractAgent ;
2121import modelengine .fel .engine .operators .prompts .Prompts ;
22+ import modelengine .fel .tool .mcp .client .McpClient ;
23+ import modelengine .fel .tool .mcp .client .McpClientFactory ;
24+ import modelengine .fel .tool .mcp .entity .Tool ;
2225import modelengine .fel .tool .model .transfer .ToolData ;
26+ import modelengine .fit .jober .aipp .util .McpUtils ;
27+ import modelengine .fitframework .inspection .Validation ;
2328import modelengine .jade .store .service .ToolService ;
2429import modelengine .fit .jade .aipp .model .dto .ModelAccessInfo ;
2530import modelengine .fit .jade .aipp .model .service .AippModelCenter ;
6065import modelengine .fitframework .util .StringUtils ;
6166import modelengine .fitframework .util .UuidUtils ;
6267
68+ import java .io .IOException ;
6369import java .util .ArrayList ;
6470import java .util .Collections ;
6571import java .util .HashMap ;
6975import java .util .Optional ;
7076import java .util .concurrent .ConcurrentHashMap ;
7177import java .util .stream .Collectors ;
78+ import java .util .stream .Stream ;
7279
7380/**
7481 * LLM 组件实现
@@ -101,6 +108,7 @@ public class LlmComponent implements FlowableService {
101108 private final AippModelCenter aippModelCenter ;
102109 private final PromptBuilderChain promptBuilderChain ;
103110 private final AppTaskInstanceService appTaskInstanceService ;
111+ private final McpClientFactory mcpClientFactory ;
104112
105113 /**
106114 * 大模型节点构造器,内部通过提供的 agent 和 tool 构建智能体工作流。
@@ -114,6 +122,7 @@ public class LlmComponent implements FlowableService {
114122 * @param aippModelCenter 表示模型中心的 {@link AippModelCenter}。
115123 * @param promptBuilderChain 表示提示器构造器职责链的 {@link PromptBuilderChain}。
116124 * @param appTaskInstanceService 表示任务实例服务的 {@link AppTaskInstanceService}。
125+ * @param mcpClientFactory 表示大模型上下文客户端工厂的 {@link McpClientFactory}。
117126 */
118127 public LlmComponent (FlowInstanceService flowInstanceService ,
119128 @ Fit ToolService toolService ,
@@ -123,7 +132,8 @@ public LlmComponent(FlowInstanceService flowInstanceService,
123132 @ Fit (alias = "json" ) ObjectSerializer serializer ,
124133 AippModelCenter aippModelCenter ,
125134 PromptBuilderChain promptBuilderChain ,
126- AppTaskInstanceService appTaskInstanceService ) {
135+ AppTaskInstanceService appTaskInstanceService ,
136+ McpClientFactory mcpClientFactory ) {
127137 this .flowInstanceService = flowInstanceService ;
128138 this .toolService = toolService ;
129139 this .aippLogService = aippLogService ;
@@ -139,6 +149,7 @@ public LlmComponent(FlowInstanceService flowInstanceService,
139149 .close ();
140150 this .promptBuilderChain = promptBuilderChain ;
141151 this .appTaskInstanceService = appTaskInstanceService ;
152+ this .mcpClientFactory = notNull (mcpClientFactory , "The mcp client factory cannot be null." );
142153 }
143154
144155 /**
@@ -177,6 +188,7 @@ public List<Map<String, Object>> handleTask(List<Map<String, Object>> flowData)
177188 StreamMsgSender streamMsgSender =
178189 new StreamMsgSender (this .aippLogStreamService , this .serializer , path , msgId , instId );
179190 streamMsgSender .sendKnowledge (promptMessage .getMetadata (), businessData );
191+ ChatOption chatOption = buildChatOptions (businessData );
180192 agentFlow .converse ()
181193 .bind ((acc , chunk ) -> {
182194 if (firstTokenFlag [0 ]) {
@@ -195,7 +207,8 @@ public List<Map<String, Object>> handleTask(List<Map<String, Object>> flowData)
195207 .doOnConsume (msg -> llmOutputConsumer (llmMeta , msg , promptMessage .getMetadata ()))
196208 .doOnError (throwable -> doOnAgentError (llmMeta ,
197209 throwable .getCause () == null ? throwable .getMessage () : throwable .getCause ().getMessage ()))
198- .bind (buildChatOptions (businessData ))
210+ .bind (chatOption )
211+ .bind (AippConst .TOOLS_KEY , chatOption .tools ())
199212 .offer (Tip .fromArray (promptMessage .getSystemMessage (), promptMessage .getHumanMessage ()));
200213 log .info ("[perf] [{}] handleTask end, instId={}" , System .currentTimeMillis (), instId );
201214 return flowData ;
@@ -393,10 +406,6 @@ private String getFilePath(Map<String, Object> businessData) {
393406 * @return 返回表示自定义参数。
394407 */
395408 private ChatOption buildChatOptions (Map <String , Object > businessData ) {
396- List <String > skillNameList = new ArrayList <>(ObjectUtils .cast (businessData .get ("tools" )));
397- if (businessData .containsKey ("workflows" )) {
398- skillNameList .addAll (ObjectUtils .cast (businessData .get ("workflows" )));
399- }
400409 String model = ObjectUtils .cast (businessData .get ("model" ));
401410 Map <String , String > accessInfo = ObjectUtils .nullIf (ObjectUtils .cast (businessData .get ("accessInfo" )),
402411 MapBuilder .<String , String >get ().put ("serviceName" , model ).put ("tag" , "INTERNAL" ).build ());
@@ -413,10 +422,50 @@ private ChatOption buildChatOptions(Map<String, Object> businessData) {
413422 .secureConfig (modelAccessInfo .isSystemModel () ? null : SecureConfig .custom ().ignoreTrust (true ).build ())
414423 .apiKey (modelAccessInfo .getAccessKey ())
415424 .temperature (ObjectUtils .cast (businessData .get ("temperature" )))
416- .tools (this .buildToolInfos (skillNameList ))
425+ .tools (this .buildToolInfos (businessData ))
417426 .build ();
418427 }
419428
429+ private List <ToolInfo > buildToolInfos (Map <String , Object > businessData ) {
430+ List <String > skillNameList = new ArrayList <>(ObjectUtils .cast (businessData .get ("tools" )));
431+ if (businessData .containsKey ("workflows" )) {
432+ skillNameList .addAll (ObjectUtils .cast (businessData .get ("workflows" )));
433+ }
434+ Map <String , Object > mcpServersConfig = ObjectUtils .cast (businessData .get (AippConst .MCP_SERVERS_KEY ));
435+
436+ return Stream .concat (this .buildToolInfos (skillNameList ).stream (),
437+ this .buildMcpToolInfos (mcpServersConfig ).stream ()).collect (Collectors .toList ());
438+ }
439+
440+ private List <ToolInfo > buildMcpToolInfos (Map <String , Object > mcpServersConfig ) {
441+ List <ToolInfo > result = new ArrayList <>();
442+ ObjectUtils .nullIf (mcpServersConfig , new HashMap <String , Object >()).forEach ((serverName , value ) -> {
443+ Map <String , Object > serverConfig = ObjectUtils .cast (value );
444+ String url = Validation .notBlank (ObjectUtils .cast (serverConfig .get (AippConst .MCP_SERVER_URL_KEY )),
445+ "The mcp url should not be empty." );
446+
447+ try (McpClient mcpClient = this .mcpClientFactory .create (McpUtils .getBaseUrl (url ),
448+ McpUtils .getSseEndpoint (url ))) {
449+ mcpClient .initialize ();
450+ List <Tool > tools = mcpClient .getTools ();
451+ result .addAll (tools .stream ()
452+ .map (tool -> ToolInfo .custom ()
453+ .name (buildUniqueToolName (AippConst .MCP_SERVER_TYPE , serverName , tool .getName ()))
454+ .description (tool .getDescription ())
455+ .parameters (tool .getInputSchema ())
456+ .extensions (MapBuilder .<String , Object >get ()
457+ .put (AippConst .MCP_SERVER_KEY , serverConfig )
458+ .put (AippConst .TOOL_REAL_NAME , tool .getName ())
459+ .build ())
460+ .build ())
461+ .toList ());
462+ } catch (IOException exception ) {
463+ throw new AippException (AippErrCode .CALL_MCP_SERVER_FAILED , exception .getMessage ());
464+ }
465+ });
466+ return result ;
467+ }
468+
420469 private List <ToolInfo > buildToolInfos (List <String > skillNameList ) {
421470 return skillNameList .stream ()
422471 .map (this .toolService ::getTool )
@@ -427,12 +476,21 @@ private List<ToolInfo> buildToolInfos(List<String> skillNameList) {
427476
428477 private ToolInfo buildToolInfo (ToolData toolData ) {
429478 return ToolInfo .custom ()
430- .name (toolData .getUniqueName ())
479+ .name (buildUniqueToolName (AippConst .STORE_SERVER_TYPE ,
480+ AippConst .STORE_SERVER_NAME ,
481+ toolData .getUniqueName ()))
431482 .description (toolData .getDescription ())
432483 .parameters (new HashMap <>(toolData .getSchema ()))
484+ .extensions (MapBuilder .<String , Object >get ()
485+ .put (AippConst .TOOL_REAL_NAME , toolData .getUniqueName ())
486+ .build ())
433487 .build ();
434488 }
435489
490+ private static String buildUniqueToolName (String type , String serverName , String toolName ) {
491+ return String .format ("%s_%s_%s" , type , serverName , toolName );
492+ }
493+
436494 public static boolean checkEnableLog (Map <String , Object > businessData ) {
437495 Object value = businessData .get (AippConst .BS_LLM_ENABLE_LOG );
438496 if (value == null ) {
0 commit comments