Skip to content

Commit 5e99770

Browse files
committed
feat: enhance MCP tool execution with unique ID generation and improved file handling
1 parent bb665b5 commit 5e99770

File tree

2 files changed

+66
-23
lines changed

2 files changed

+66
-23
lines changed

apps/application/flow/step_node/ai_chat_step_node/impl/base_chat_node.py

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
from application.flow.step_node.ai_chat_step_node.i_chat_node import IChatNode
2828
from application.flow.tools import Reasoning
2929
from common.utils.logger import maxkb_logger
30+
from common.utils.rsa_util import rsa_long_decrypt
3031
from common.utils.tool_code import ToolExecutor
32+
from maxkb.const import CONFIG
3133
from models_provider.models import Model
3234
from models_provider.tools import get_model_credential, get_model_instance_by_model_workspace_id
3335
from tools.models import Tool
@@ -283,25 +285,14 @@ def _handle_mcp_request(self, mcp_enable, tool_enable, mcp_source, mcp_servers,
283285
self.context['execute_ids'] = []
284286
for tool_id in tool_ids:
285287
tool = QuerySet(Tool).filter(id=tool_id).first()
286-
executor = ToolExecutor(sandbox=True)
287-
code = executor.generate_mcp_server_code(tool.code)
288-
_id = uuid.uuid7()
288+
executor = ToolExecutor(CONFIG.get('SANDBOX'))
289+
if tool.init_params is not None:
290+
params = json.loads(rsa_long_decrypt(tool.init_params))
291+
else:
292+
params = {}
293+
_id, tool_config = executor.get_tool_mcp_config(tool.code, params)
294+
289295
self.context['execute_ids'].append(_id)
290-
code_path = f'{executor.sandbox_path}/execute/{_id}.py'
291-
with open(code_path, 'w') as f:
292-
f.write(code)
293-
os.system(f"chown {executor.user}:root {code_path}")
294-
295-
tool_config = {
296-
'command': 'su',
297-
'args': [
298-
'-s', sys.executable,
299-
'-c', f"exec(open('{code_path}', 'r').read())",
300-
executor.user,
301-
],
302-
'cwd': executor.sandbox_path,
303-
'transport': 'stdio',
304-
}
305296
mcp_servers_config[str(tool.id)] = tool_config
306297

307298
if len(mcp_servers_config) > 0:
@@ -347,7 +338,7 @@ def reset_message_list(message_list: List[BaseMessage], answer_text):
347338
def get_details(self, index: int, **kwargs):
348339
# 删除临时生成的MCP代码文件
349340
if self.context.get('execute_ids'):
350-
executor = ToolExecutor(sandbox=True)
341+
executor = ToolExecutor(CONFIG.get('SANDBOX'))
351342
# 清理工具代码文件,延时删除,避免文件被占用
352343
for tool_id in self.context.get('execute_ids'):
353344
code_path = f'{executor.sandbox_path}/execute/{tool_id}.py'

apps/common/utils/tool_code.py

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def exec_code(self, code_str, keywords):
8383
return result.get('data')
8484
raise Exception(result.get('msg'))
8585

86-
def _generate_mcp_server_code(self, _code):
86+
def _generate_mcp_server_code(self, _code, params):
8787
self.validate_banned_keywords(_code)
8888

8989
# 解析代码,提取导入语句和函数定义
@@ -100,7 +100,31 @@ def _generate_mcp_server_code(self, _code):
100100
if isinstance(node, ast.Import) or isinstance(node, ast.ImportFrom):
101101
imports.append(ast.unparse(node))
102102
elif isinstance(node, ast.FunctionDef):
103-
# 为函数添加 @mcp.tool() 装饰器
103+
# 修改函数参数以包含 params 中的默认值
104+
func_name = node.name
105+
if func_name in params:
106+
func_params = params[func_name]
107+
# 为函数参数设置默认值
108+
for i, arg in enumerate(node.args.args):
109+
arg_name = arg.arg
110+
if arg_name in func_params:
111+
# 创建默认值节点
112+
default_value = func_params[arg_name]
113+
if isinstance(default_value, str):
114+
default_node = ast.Constant(value=default_value)
115+
elif isinstance(default_value, (int, float, bool)):
116+
default_node = ast.Constant(value=default_value)
117+
else:
118+
default_node = ast.Constant(value=str(default_value))
119+
120+
# 添加到defaults列表
121+
if not hasattr(node.args, 'defaults') or node.args.defaults is None:
122+
node.args.defaults = []
123+
# 确保defaults列表长度正确
124+
while len(node.args.defaults) < len(node.args.args):
125+
node.args.defaults.insert(0, None)
126+
node.args.defaults[i] = default_node
127+
104128
func_code = ast.unparse(node)
105129
functions.append(f"@mcp.tool()\n{func_code}\n")
106130
else:
@@ -116,9 +140,9 @@ def _generate_mcp_server_code(self, _code):
116140

117141
return "\n".join(code_parts)
118142

119-
def generate_mcp_server_code(self, code_str):
143+
def generate_mcp_server_code(self, code_str, params):
120144
python_paths = CONFIG.get_sandbox_python_package_paths().split(',')
121-
code = self._generate_mcp_server_code(code_str)
145+
code = self._generate_mcp_server_code(code_str, params)
122146
return f"""
123147
import os
124148
import sys
@@ -132,6 +156,34 @@ def generate_mcp_server_code(self, code_str):
132156
exec({dedent(code)!a})
133157
"""
134158

159+
def get_tool_mcp_config(self, code, params):
160+
code = self.generate_mcp_server_code(code, params)
161+
162+
_id = uuid.uuid7()
163+
code_path = f'{self.sandbox_path}/execute/{_id}.py'
164+
with open(code_path, 'w') as f:
165+
f.write(code)
166+
if self.sandbox:
167+
os.system(f"chown {self.user}:root {code_path}")
168+
169+
tool_config = {
170+
'command': 'su',
171+
'args': [
172+
'-s', sys.executable,
173+
'-c', f"exec(open('{code_path}', 'r').read())",
174+
self.user,
175+
],
176+
'cwd': self.sandbox_path,
177+
'transport': 'stdio',
178+
}
179+
else:
180+
tool_config = {
181+
'command': sys.executable,
182+
'args': [code_path],
183+
'transport': 'stdio',
184+
}
185+
return _id, tool_config
186+
135187
def _exec_sandbox(self, _code, _id):
136188
exec_python_file = f'{self.sandbox_path}/execute/{_id}.py'
137189
with open(exec_python_file, 'w') as file:

0 commit comments

Comments
 (0)