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 = this .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,40 @@ 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 ().map (tool -> buildMcpToolInfo (serverName , tool , serverConfig )).toList ());
452+ } catch (IOException exception ) {
453+ throw new AippException (AippErrCode .CALL_MCP_SERVER_FAILED , exception .getMessage ());
454+ }
455+ });
456+ return result ;
457+ }
458+
420459 private List <ToolInfo > buildToolInfos (List <String > skillNameList ) {
421460 return skillNameList .stream ()
422461 .map (this .toolService ::getTool )
@@ -427,12 +466,39 @@ private List<ToolInfo> buildToolInfos(List<String> skillNameList) {
427466
428467 private ToolInfo buildToolInfo (ToolData toolData ) {
429468 return ToolInfo .custom ()
430- .name (toolData .getUniqueName ())
469+ .name (buildUniqueToolName (AippConst .STORE_SERVER_TYPE ,
470+ AippConst .STORE_SERVER_NAME ,
471+ toolData .getUniqueName ()))
431472 .description (toolData .getDescription ())
432473 .parameters (new HashMap <>(toolData .getSchema ()))
474+ .extensions (MapBuilder .<String , Object >get ()
475+ .put (AippConst .TOOL_REAL_NAME , toolData .getUniqueName ())
476+ .build ())
477+ .build ();
478+ }
479+
480+ private static ToolInfo buildMcpToolInfo (String serverName , Tool tool , Map <String , Object > serverConfig ) {
481+ return ToolInfo .custom ()
482+ .name (buildUniqueToolName (AippConst .MCP_SERVER_TYPE , serverName , tool .getName ()))
483+ .description (tool .getDescription ())
484+ .parameters (tool .getInputSchema ())
485+ .extensions (MapBuilder .<String , Object >get ()
486+ .put (AippConst .MCP_SERVER_KEY , serverConfig )
487+ .put (AippConst .TOOL_REAL_NAME , tool .getName ())
488+ .build ())
433489 .build ();
434490 }
435491
492+ private static String buildUniqueToolName (String type , String serverName , String toolName ) {
493+ return StringUtils .format ("{0}_{1}_{2}" , type , serverName , toolName );
494+ }
495+
496+ /**
497+ * 判断是否启用日志。
498+ *
499+ * @param businessData 表示业务上下文数据的 {@link Map}{@code <}{@link String}{@code , }{@link Object}{@code >}。
500+ * @return 表示是否启用日志的 {@code boolean}。
501+ */
436502 public static boolean checkEnableLog (Map <String , Object > businessData ) {
437503 Object value = businessData .get (AippConst .BS_LLM_ENABLE_LOG );
438504 if (value == null ) {
0 commit comments