11# -*- coding: utf-8 -*-
2+ import asyncio
23import io
34import json
45import os
56import pickle
67import re
8+ from typing import Dict
79
810import uuid_utils .compat as uuid
911from django .core import validators
1214from django .http import HttpResponse
1315from django .utils import timezone
1416from django .utils .translation import gettext_lazy as _
17+ from langchain_mcp_adapters .client import MultiServerMCPClient
1518from pylint .lint import Run
1619from pylint .reporters import JSON2Reporter
1720from rest_framework import serializers , status
2225from common .field .common import UploadedImageField
2326from common .result import result
2427from common .utils .common import get_file_content
28+ from common .utils .logger import maxkb_logger
2529from common .utils .rsa_util import rsa_long_decrypt , rsa_long_encrypt
2630from common .utils .tool_code import ToolExecutor
2731from knowledge .models import File , FileSourceType
@@ -103,6 +107,18 @@ def encryption(message: str):
103107 return pre_str + content + end_str
104108
105109
110+ def validate_mcp_config (servers : Dict ):
111+ async def validate ():
112+ client = MultiServerMCPClient (servers )
113+ await client .get_tools ()
114+
115+ try :
116+ asyncio .run (validate ())
117+ except Exception as e :
118+ maxkb_logger .error (f"validate mcp config error: { e } , servers: { servers } " )
119+ raise serializers .ValidationError (_ ('MCP configuration is invalid' ))
120+
121+
106122class ToolModelSerializer (serializers .ModelSerializer ):
107123 class Meta :
108124 model = Tool
@@ -201,6 +217,131 @@ class PylintInstance(serializers.Serializer):
201217
202218
203219class ToolSerializer (serializers .Serializer ):
220+ class Query (serializers .Serializer ):
221+ workspace_id = serializers .CharField (required = True , label = _ ('workspace id' ))
222+ folder_id = serializers .CharField (required = False , allow_blank = True , allow_null = True , label = _ ('folder id' ))
223+ name = serializers .CharField (required = False , allow_null = True , allow_blank = True , label = _ ('tool name' ))
224+ user_id = serializers .UUIDField (required = False , allow_null = True , label = _ ('user id' ))
225+ scope = serializers .CharField (required = True , label = _ ('scope' ))
226+ tool_type = serializers .CharField (required = False , label = _ ('tool type' ), allow_null = True , allow_blank = True )
227+ create_user = serializers .UUIDField (required = False , label = _ ('create user' ), allow_null = True )
228+
229+ def get_query_set (self , workspace_manage , is_x_pack_ee ):
230+ tool_query_set = QuerySet (Tool ).filter (workspace_id = self .data .get ('workspace_id' ))
231+ folder_query_set = QuerySet (ToolFolder )
232+ default_query_set = QuerySet (Tool )
233+
234+ workspace_id = self .data .get ('workspace_id' )
235+ user_id = self .data .get ('user_id' )
236+ scope = self .data .get ('scope' )
237+ tool_type = self .data .get ('tool_type' )
238+ desc = self .data .get ('desc' )
239+ name = self .data .get ('name' )
240+ folder_id = self .data .get ('folder_id' )
241+ create_user = self .data .get ('create_user' )
242+
243+ if workspace_id is not None :
244+ folder_query_set = folder_query_set .filter (workspace_id = workspace_id )
245+ default_query_set = default_query_set .filter (workspace_id = workspace_id )
246+ if folder_id is not None :
247+ folder_query_set = folder_query_set .filter (parent = folder_id )
248+ default_query_set = default_query_set .filter (folder_id = folder_id )
249+ if name is not None :
250+ folder_query_set = folder_query_set .filter (name__icontains = name )
251+ default_query_set = default_query_set .filter (name__icontains = name )
252+ if desc is not None :
253+ folder_query_set = folder_query_set .filter (desc__icontains = desc )
254+ default_query_set = default_query_set .filter (desc__icontains = desc )
255+ if create_user is not None :
256+ tool_query_set = tool_query_set .filter (user_id = create_user )
257+ folder_query_set = folder_query_set .filter (user_id = create_user )
258+
259+ default_query_set = default_query_set .order_by ("-create_time" )
260+
261+ if scope is not None :
262+ tool_query_set = tool_query_set .filter (scope = scope )
263+ if tool_type :
264+ tool_query_set = tool_query_set .filter (tool_type = tool_type )
265+
266+ query_set_dict = {
267+ 'folder_query_set' : folder_query_set ,
268+ 'tool_query_set' : tool_query_set ,
269+ 'default_query_set' : default_query_set ,
270+ }
271+ if not workspace_manage :
272+ query_set_dict ['workspace_user_resource_permission_query_set' ] = QuerySet (
273+ WorkspaceUserResourcePermission ).filter (
274+ auth_target_type = "TOOL" ,
275+ workspace_id = workspace_id ,
276+ user_id = user_id
277+ )
278+ return query_set_dict
279+
280+ def get_authorized_query_set (self ):
281+ default_query_set = QuerySet (Tool )
282+ tool_type = self .data .get ('tool_type' )
283+ desc = self .data .get ('desc' )
284+ name = self .data .get ('name' )
285+ create_user = self .data .get ('create_user' )
286+
287+ default_query_set = default_query_set .filter (workspace_id = 'None' )
288+ default_query_set = default_query_set .filter (scope = ToolScope .SHARED )
289+ if name is not None :
290+ default_query_set = default_query_set .filter (name__icontains = name )
291+ if desc is not None :
292+ default_query_set = default_query_set .filter (desc__icontains = desc )
293+ if create_user is not None :
294+ default_query_set = default_query_set .filter (user_id = create_user )
295+ if tool_type :
296+ default_query_set = default_query_set .filter (tool_type = tool_type )
297+
298+ default_query_set = default_query_set .order_by ("-create_time" )
299+
300+ return default_query_set
301+
302+ @staticmethod
303+ def is_x_pack_ee ():
304+ workspace_user_role_mapping_model = DatabaseModelManage .get_model ("workspace_user_role_mapping" )
305+ role_permission_mapping_model = DatabaseModelManage .get_model ("role_permission_mapping_model" )
306+ return workspace_user_role_mapping_model is not None and role_permission_mapping_model is not None
307+
308+ def get_tools (self ):
309+ self .is_valid (raise_exception = True )
310+
311+ workspace_manage = is_workspace_manage (self .data .get ('user_id' ), self .data .get ('workspace_id' ))
312+ is_x_pack_ee = self .is_x_pack_ee ()
313+ results = native_search (
314+ self .get_query_set (workspace_manage , is_x_pack_ee ),
315+ get_file_content (
316+ os .path .join (
317+ PROJECT_DIR ,
318+ "apps" , "tools" , 'sql' ,
319+ 'list_tool.sql' if workspace_manage else (
320+ 'list_tool_user_ee.sql' if is_x_pack_ee else 'list_tool_user.sql'
321+ )
322+ )
323+ ),
324+ )
325+
326+ get_authorized_tool = DatabaseModelManage .get_model ("get_authorized_tool" )
327+ shared_queryset = QuerySet (Tool ).none ()
328+ if get_authorized_tool is not None :
329+ shared_queryset = self .get_authorized_query_set ()
330+ shared_queryset = get_authorized_tool (shared_queryset , self .data .get ('workspace_id' ))
331+
332+ return {
333+ 'shared_tools' : [
334+ ToolModelSerializer (data ).data for data in shared_queryset
335+ ],
336+ 'tools' : [
337+ {
338+ ** tool ,
339+ 'input_field_list' : json .loads (tool .get ('input_field_list' , '[]' )),
340+ 'init_field_list' : json .loads (tool .get ('init_field_list' , '[]' )),
341+ } for tool in results if tool ['resource_type' ] == 'tool'
342+ ],
343+ }
344+
204345 class Create (serializers .Serializer ):
205346 user_id = serializers .UUIDField (required = True , label = _ ('user id' ))
206347 workspace_id = serializers .CharField (required = True , label = _ ('workspace id' ))
@@ -212,6 +353,10 @@ def insert(self, instance, with_valid=True):
212353 ToolCreateRequest (data = instance ).is_valid (raise_exception = True )
213354 # 校验代码是否包括禁止的关键字
214355 ToolExecutor ().validate_banned_keywords (instance .get ('code' , '' ))
356+ # 校验mcp json
357+ if instance .get ('tool_type' ) == ToolType .MCP .value :
358+ validate_mcp_config (json .loads (instance .get ('code' )))
359+
215360 tool_id = uuid .uuid7 ()
216361 Tool (
217362 id = tool_id ,
@@ -223,6 +368,7 @@ def insert(self, instance, with_valid=True):
223368 input_field_list = instance .get ('input_field_list' , []),
224369 init_field_list = instance .get ('init_field_list' , []),
225370 scope = instance .get ('scope' , ToolScope .WORKSPACE ),
371+ tool_type = instance .get ('tool_type' , ToolType .CUSTOM ),
226372 folder_id = instance .get ('folder_id' , self .data .get ('workspace_id' )),
227373 is_active = False
228374 ).save ()
@@ -326,6 +472,10 @@ def edit(self, instance, with_valid=True):
326472 ToolEditRequest (data = instance ).is_valid (raise_exception = True )
327473 # 校验代码是否包括禁止的关键字
328474 ToolExecutor ().validate_banned_keywords (instance .get ('code' , '' ))
475+ # 校验mcp json
476+ if instance .get ('tool_type' ) == ToolType .MCP .value :
477+ validate_mcp_config (json .loads (instance .get ('code' )))
478+
329479 if not QuerySet (Tool ).filter (id = self .data .get ('id' )).exists ():
330480 raise serializers .ValidationError (_ ('Tool not found' ))
331481
@@ -574,6 +724,7 @@ class Query(serializers.Serializer):
574724 name = serializers .CharField (required = False , allow_null = True , allow_blank = True , label = _ ('tool name' ))
575725 user_id = serializers .UUIDField (required = False , allow_null = True , label = _ ('user id' ))
576726 scope = serializers .CharField (required = True , label = _ ('scope' ))
727+ tool_type = serializers .CharField (required = False , label = _ ('tool type' ), allow_null = True , allow_blank = True )
577728 create_user = serializers .UUIDField (required = False , label = _ ('create user' ), allow_null = True )
578729
579730 def page_tool (self , current_page : int , page_size : int ):
@@ -609,6 +760,7 @@ def get_query_set(self, workspace_manage, is_x_pack_ee):
609760 workspace_id = self .data .get ('workspace_id' )
610761 user_id = self .data .get ('user_id' )
611762 scope = self .data .get ('scope' )
763+ tool_type = self .data .get ('tool_type' )
612764 desc = self .data .get ('desc' )
613765 name = self .data .get ('name' )
614766 folder_id = self .data .get ('folder_id' )
@@ -634,6 +786,8 @@ def get_query_set(self, workspace_manage, is_x_pack_ee):
634786
635787 if scope is not None :
636788 tool_query_set = tool_query_set .filter (scope = scope )
789+ if tool_type :
790+ tool_query_set = tool_query_set .filter (tool_type = tool_type )
637791
638792 query_set_dict = {
639793 'folder_query_set' : folder_query_set ,
0 commit comments