Skip to content

[Bug] Call the self-written excel generation tool in maxkb. When the amount of data is large and the response takes a long time, maxkb will cancel the call. It is normal to configure the tool in Trae #4197

@yinbiaos

Description

@yinbiaos

Contact Information

[email protected]

MaxKB Version

v1.10.3-lts

Problem Description

@tool(name = "generate_and_download_excel_tool", description = "excel工具:生成excel并返回下载地址")
@OverRide
public String generateAndDownloadExcel(@ToolParam(description = "oracle查询语句,查询列使用字段注释做为别名,可以直接使用预览数据的sql") String querySql) {
log.info("生成excel文件--->{}", querySql);
querySql = querySql.trim();
if (querySql.endsWith(";")) {
querySql = querySql.substring(0, querySql.length() - 1);
}
if (!isQuerySql(querySql)) {
throw new QzException("请输入正确的sql语句");
}
//查询数据条数
long totalCount = getTotalCount(querySql);
log.info("查询数据总条数--->{}", totalCount);

    List<String> columns = extractColumns(querySql);
    log.info("查询字段--->{}", columns);

    //生成excel字段列的表头
    List<List<String>> head = generateHead(columns);
    //查询完整数据
    querySql = String.format("SELECT * FROM ( %s ) WHERE ROWNUM <= %s", querySql, Math.min(totalCount, excelConfig.getDownloadCount()));
    log.info("查询数据内容--->{}", querySql);
    List<Map<String, Object>> datas = jdbcTemplate.queryForList(querySql);
    log.info("下载数据条数--->{}", datas.size());

    //这里需要转换一下,因为 easyExcel 不支持 Map<String,Object> , 按照表头的顺序,生成对应的行数据
    List<List<Object>> rows = datas.stream().map(m -> columns.stream().map(m::get).toList()).toList();

    //生成临时文件名和文件路径
    String fileName = String.format("output-%s.xlsx", IdUtil.getSnowflakeNextIdStr());
    String filePath = System.getProperty("java.io.tmpdir") + fileName;
    // 写入 Excel
    EasyExcel.write(filePath).sheet("Sheet1")
            //设置自动合并表头
            .automaticMergeHead(true)
            // 设置列宽自适应(不太精准)

// .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
// 设置表头
.head(head)
// 写入数据
.doWrite(rows);
//生成文件下载地址
String downloadUrl = excelConfig.getDownloadPath() + fileName;
log.info("下载文件地址--->{}", downloadUrl);
return downloadUrl;
}

public List extractColumns(String sql) {
List columns = new ArrayList<>();
// 1. 给 SQL 加限制,只查询表结构,不真正查询数据
String limitedSql = String.format("SELECT * FROM ( %s ) WHERE 1=0", sql);
log.info("查询字段的SQL--->{}", limitedSql);
// 2. 执行 SQL 获取字段元数据
try (Connection conn = dataSource.getConnection(); PreparedStatement ps = conn.prepareStatement(limitedSql); ResultSet rs = ps.executeQuery()) {
ResultSetMetaData meta = rs.getMetaData();
int count = meta.getColumnCount();
for (int i = 1; i <= count; i++) {
//字段别名 getColumnLabel 原始字段名是 getColumnName
columns.add(meta.getColumnLabel(i));
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
return columns;
}

Steps to Reproduce

1、启动这个工具服务
2、maxkb中配置调用工具
{
"McpServer": {
"url": "http://172.16.55.251:7390/sse",
"transport":"sse"
}
}

3、调试运行,帮我导出xxx数据

The expected correct result

java.lang.RuntimeException: java.sql.SQLException: interrupt
at com.qzdatasoft.mcpserver.tools.impl.ExcelToolsImpl.extractColumns(ExcelToolsImpl.java:196) ~[classes/:na]
at com.qzdatasoft.mcpserver.tools.impl.ExcelToolsImpl.generateAndDownloadExcel(ExcelToolsImpl.java:72) ~[classes/:na]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:359) ~[spring-aop-6.2.8.jar:6.2.8]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) ~[spring-aop-6.2.8.jar:6.2.8]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-6.2.8.jar:6.2.8]
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:89) ~[spring-aop-6.2.8.jar:6.2.8]
at com.qzdatasoft.mcpserver.config.McpExceptionHandlerAspect.handleException(McpExceptionHandlerAspect.java:24) ~[classes/:na]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:642) ~[spring-aop-6.2.8.jar:6.2.8]
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:632) ~[spring-aop-6.2.8.jar:6.2.8]
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:71) ~[spring-aop-6.2.8.jar:6.2.8]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:173) ~[spring-aop-6.2.8.jar:6.2.8]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-6.2.8.jar:6.2.8]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.2.8.jar:6.2.8]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:728) ~[spring-aop-6.2.8.jar:6.2.8]
at com.qzdatasoft.mcpserver.tools.impl.ExcelToolsImpl$$SpringCGLIB$$0.generateAndDownloadExcel() ~[classes/:na]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
at org.springframework.ai.tool.method.MethodToolCallback.callMethod(MethodToolCallback.java:166) ~[spring-ai-model-1.1.0-M2.jar:1.1.0-M2]
at org.springframework.ai.tool.method.MethodToolCallback.call(MethodToolCallback.java:109) ~[spring-ai-model-1.1.0-M2.jar:1.1.0-M2]
at org.springframework.ai.mcp.McpToolUtils.lambda$toSharedSyncToolSpecification$5(McpToolUtils.java:241) ~[spring-ai-mcp-1.1.0-M2.jar:1.1.0-M2]
at org.springframework.ai.mcp.McpToolUtils.lambda$toSyncToolSpecification$2(McpToolUtils.java:204) ~[spring-ai-mcp-1.1.0-M2.jar:1.1.0-M2]
at org.springframework.ai.mcp.McpToolUtils.lambda$toAsyncToolSpecification$6(McpToolUtils.java:358) ~[spring-ai-mcp-1.1.0-M2.jar:1.1.0-M2]
at reactor.core.publisher.MonoCallable.call(MonoCallable.java:72) ~[reactor-core-3.7.7.jar:3.7.7]
at reactor.core.publisher.FluxSubscribeOnCallable$CallableSubscribeOnSubscription.run(FluxSubscribeOnCallable.java:228) ~[reactor-core-3.7.7.jar:3.7.7]
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68) ~[reactor-core-3.7.7.jar:3.7.7]
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28) ~[reactor-core-3.7.7.jar:3.7.7]
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) ~[na:na]
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[na:na]
at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]
Caused by: java.sql.SQLException: interrupt
at com.alibaba.druid.pool.DruidDataSource.getConnectionInternal(DruidDataSource.java:1739) ~[druid-1.2.23.jar:na]
at com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource.java:1502) ~[druid-1.2.23.jar:na]
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1482) ~[druid-1.2.23.jar:na]
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1463) ~[druid-1.2.23.jar:na]
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:83) ~[druid-1.2.23.jar:na]
at com.qzdatasoft.mcpserver.tools.impl.ExcelToolsImpl.extractColumns(ExcelToolsImpl.java:188) ~[classes/:na]
... 34 common frames omitted
Caused by: java.lang.InterruptedException: null
at java.base/java.util.concurrent.locks.ReentrantLock$Sync.lockInterruptibly(ReentrantLock.java:159) ~[na:na]
at java.base/java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:372) ~[na:na]
at com.alibaba.druid.pool.DruidDataSource.getConnectionInternal(DruidDataSource.java:1736) ~[druid-1.2.23.jar:na]
... 39 common frames omitted

Related log output

Additional Information

No response

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions