feat: 添加数据库管理平台MCP Server
新增lzwcai-mcp-agile-db项目,提供数据库管理、表操作、数据CRUD、 API密钥管理、技能与工具管理等功能。 包含33个工具: - 数据源管理:创建、更新、删除数据源 - 数据库与表管理:表结构操作、数据查询等 - API密钥管理:密钥创建、权限管理等 - 技能与工具管理:SQL工具创建、配置更新等 - 数据导入和SQL执行功能 添加了完整的README文档说明安装使用方法, 以及Python 3.12版本支持和基本项目结构。
This commit is contained in:
BIN
lzwcai_mcp_sqlexecutor/__pycache__/main.cpython-312.pyc
Normal file
BIN
lzwcai_mcp_sqlexecutor/__pycache__/main.cpython-312.pyc
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -1,25 +1,289 @@
|
||||
2026-05-25 14:32:22 - root - INFO - [logger_config.py:151] - 日志系统初始化完成 - 日志目录: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\logs
|
||||
2026-05-25 14:32:22 - root - INFO - [logger_config.py:152] - 日志配置 - 级别: INFO, 文件大小限制: 10MB, 备份数量: 5
|
||||
2026-05-25 14:32:22 - mcp_services - INFO - [main.py:352] - 开始运行 MCP SQL Executor 服务器
|
||||
2026-05-25 14:32:22 - mcp_services - INFO - [main.py:304] - ============================================================
|
||||
2026-05-25 14:32:22 - mcp_services - INFO - [main.py:305] - 正在启动 MCP 服务器: lzwcai-mcp-sqlexecutor
|
||||
2026-05-25 14:32:22 - mcp_services - INFO - [main.py:306] - 版本: 0.1.0
|
||||
2026-05-25 14:32:22 - mcp_services - INFO - [main.py:307] - ============================================================
|
||||
2026-05-25 14:32:22 - mcp_services - INFO - [main.py:311] - 环境配置 - Database ID: 162
|
||||
2026-05-25 14:32:22 - mcp_services - INFO - [main.py:312] - 环境配置 - Skill ID: 2008360664955854850
|
||||
2026-05-25 14:32:22 - mcp_services - INFO - [main.py:313] - 环境配置 - Backend Base URL: http://192.168.2.236:8088
|
||||
2026-05-25 14:32:22 - mcp_services - INFO - [main.py:314] - ============================================================
|
||||
2026-05-25 14:32:22 - mcp_services - INFO - [main.py:319] - MCP 服务器已启动,等待客户端连接...
|
||||
2026-05-25 14:32:35 - mcp.server.lowlevel.server - INFO - [server.py:720] - Processing request of type ListToolsRequest
|
||||
2026-05-25 14:32:35 - mcp_services - INFO - [main.py:156] - 收到列出工具请求
|
||||
2026-05-25 14:32:35 - mcp_services - INFO - [main.py:119] - 初始化查询配置(数据源: api)...
|
||||
2026-05-25 14:32:35 - mcp_services - INFO - [main.py:283] - 调用第三方API,skill_id: 2008360664955854850
|
||||
2026-05-25 14:32:35 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:71] - 正在调用API: http://192.168.2.236:8088/datasource/skill/getBySkillId/2008360664955854850
|
||||
2026-05-25 14:32:35 - httpx - INFO - [_client.py:1025] - HTTP Request: GET http://192.168.2.236:8088/datasource/skill/getBySkillId/2008360664955854850 "HTTP/1.1 200 "
|
||||
2026-05-25 14:32:35 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:85] - API调用成功: http://192.168.2.236:8088/datasource/skill/getBySkillId/2008360664955854850
|
||||
2026-05-25 14:32:35 - mcp_services - INFO - [main.py:288] - 成功{'msg': '查询成功', 'code': 200, 'data': [{'id': '2008360824029028354', 'createBy': 'wxl06', 'createTime': '2026-01-06 10:11:49', 'updateBy': 'wxl06', 'updateTime': '2026-01-06 10:11:59', 'serviceId': '2008360664960049153', 'uniqueName': '查询订单信息', 'name': 'chaxundingdanxinxi_3acda9b4', 'description': 'chaxundingdanxinxi_3acda9b4: 查询订单表中的订单信息,包括订单号、产品名称、数量、购买客户、备注和状态等关键信息', 'visualizable': 1, 'toolPrompt': '查询成功,返回 6 行数据,执行时间: 4ms', 'toolType': 'sql', 'datasourceId': '162', 'sqlTemplate': 'SELECT order_id, product_name, quantity, customer, remarks, status FROM orders WHERE 1=1', 'sqlParams': '{"type":"object","required":[],"properties":{}}', 'resultType': 'list', 'sourceType': 'ai', 'trainingTaskId': None, 'tableMetadataIds': '', 'executionCount': 0, 'visualizationConfigs': None, 'inputJsonSchema': '{"type":"object","required":["employeeId"],"properties":{"employeeId":{"type":"number","description":"员工ID,用于标识员工的唯一数字标识符","examples":[1001,2002]},"targetDatabaseName":{"type":"string","description":"目标数据库名称"}}}', 'outputJsonSchema': '{"type":"object","properties":{"data":{"type":"array"}}}', 'lastExecutionTime': None}]}
|
||||
2026-05-25 14:32:35 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:324] - 成功处理 1 条技能数据
|
||||
2026-05-25 14:32:35 - mcp_services - INFO - [main.py:293] - 成功获取并处理 1 条数据
|
||||
2026-05-25 14:32:35 - mcp_services - INFO - [main.py:128] - API配置: 1 条
|
||||
2026-05-25 14:32:35 - mcp_services - INFO - [main.py:129] - API配置数组: [{'id': '2008360824029028354', 'businessName': 'chaxundingdanxinxi_3acda9b4', 'businessDescription': 'chaxundingdanxinxi_3acda9b4: 查询订单表中的订单信息,包括订单号、产品名称、数量、购买客户、备注和状态等关键信息', 'sqlTemplate': 'SELECT order_id, product_name, quantity, customer, remarks, status FROM orders WHERE 1=1', 'parameters': {'type': 'object', 'required': [], 'properties': {}}, 'datasourceId': '162'}]
|
||||
2026-05-25 14:32:35 - mcp_services - INFO - [main.py:165] - 成功生成 1 个 MCP 工具
|
||||
2026-05-25 16:05:46 - root - INFO - [logger_config.py:151] - 日志系统初始化完成 - 日志目录: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\logs
|
||||
2026-05-25 16:05:46 - root - INFO - [logger_config.py:152] - 日志配置 - 级别: INFO, 文件大小限制: 10MB, 备份数量: 5
|
||||
2026-05-25 16:05:46 - mcp_services - INFO - [main.py:352] - 开始运行 MCP SQL Executor 服务器
|
||||
2026-05-25 16:05:46 - mcp_services - INFO - [main.py:304] - ============================================================
|
||||
2026-05-25 16:05:46 - mcp_services - INFO - [main.py:305] - 正在启动 MCP 服务器: lzwcai-mcp-sqlexecutor
|
||||
2026-05-25 16:05:46 - mcp_services - INFO - [main.py:306] - 版本: 0.1.0
|
||||
2026-05-25 16:05:46 - mcp_services - INFO - [main.py:307] - ============================================================
|
||||
2026-05-25 16:05:46 - mcp_services - INFO - [main.py:311] - 环境配置 - Database ID: 240
|
||||
2026-05-25 16:05:46 - mcp_services - INFO - [main.py:312] - 环境配置 - Skill ID: 2058819964077572098
|
||||
2026-05-25 16:05:46 - mcp_services - INFO - [main.py:313] - 环境配置 - Backend Base URL: http://192.168.2.236:8088
|
||||
2026-05-25 16:05:46 - mcp_services - INFO - [main.py:314] - ============================================================
|
||||
2026-05-25 16:05:46 - mcp_services - INFO - [main.py:319] - MCP 服务器已启动,等待客户端连接...
|
||||
2026-05-25 16:05:49 - mcp.server.lowlevel.server - INFO - [server.py:720] - Processing request of type ListToolsRequest
|
||||
2026-05-25 16:05:49 - mcp_services - INFO - [main.py:156] - 收到列出工具请求
|
||||
2026-05-25 16:05:49 - mcp_services - INFO - [main.py:119] - 初始化查询配置(数据源: api)...
|
||||
2026-05-25 16:05:49 - mcp_services - INFO - [main.py:283] - 调用第三方API,skill_id: 2058819964077572098
|
||||
2026-05-25 16:05:49 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:71] - 正在调用API: http://192.168.2.236:8088/datasource/skill/getBySkillId/2058819964077572098
|
||||
2026-05-25 16:05:49 - httpx - INFO - [_client.py:1025] - HTTP Request: GET http://192.168.2.236:8088/datasource/skill/getBySkillId/2058819964077572098 "HTTP/1.1 200 "
|
||||
2026-05-25 16:05:49 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:85] - API调用成功: http://192.168.2.236:8088/datasource/skill/getBySkillId/2058819964077572098
|
||||
2026-05-25 16:05:49 - mcp_services - INFO - [main.py:288] - 成功{'msg': '查询成功', 'code': 200, 'data': [{'id': '2058819964287287298', 'createBy': 'wxl06', 'createTime': '2026-05-25 15:58:25', 'updateBy': None, 'updateTime': None, 'serviceId': '2058819964085960705', 'uniqueName': '前50条导入数据明细查询', 'name': 'qian50tiaodaorushujumingxichaxun_32b1d628', 'description': 'qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: 查询数据导入明细表中的前50条记录,包含原始数据、处理状态、错误原因等关键信息,用于快速预览最近的导入情况。', 'visualizable': 1, 'toolPrompt': '查询成功,返回 6 行数据,执行时间: 1ms', 'toolType': 'sql', 'datasourceId': '240', 'sqlTemplate': 'SELECT record_id AS "记录ID", task_id AS "任务ID", original_data AS "原始数据", process_result AS "处理结果", error_reason AS "错误原因", process_status AS "处理状态", target_table AS "关联目标表", created_time AS "创建时间" FROM import_record_detail LIMIT 50;', 'sqlParams': '{}', 'resultType': 'list', 'sourceType': 'ai', 'trainingTaskId': None, 'tableMetadataIds': '', 'executionCount': 0, 'visualizationConfigs': None, 'inputJsonSchema': '{"type":"object","properties":{},"required":[]}', 'outputJsonSchema': '{"type":"object","properties":{"text":{"type":"string"}},"additionalProperties":false}', 'lastExecutionTime': None}]}
|
||||
2026-05-25 16:05:49 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:324] - 成功处理 1 条技能数据
|
||||
2026-05-25 16:05:49 - mcp_services - INFO - [main.py:293] - 成功获取并处理 1 条数据
|
||||
2026-05-25 16:05:49 - mcp_services - INFO - [main.py:128] - API配置: 1 条
|
||||
2026-05-25 16:05:49 - mcp_services - INFO - [main.py:129] - API配置数组: [{'id': '2058819964287287298', 'businessName': 'qian50tiaodaorushujumingxichaxun_32b1d628', 'businessDescription': 'qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: 查询数据导入明细表中的前50条记录,包含原始数据、处理状态、错误原因等关键信息,用于快速预览最近的导入情况。', 'sqlTemplate': 'SELECT record_id AS "记录ID", task_id AS "任务ID", original_data AS "原始数据", process_result AS "处理结果", error_reason AS "错误原因", process_status AS "处理状态", target_table AS "关联目标表", created_time AS "创建时间" FROM import_record_detail LIMIT 50;', 'parameters': {}, 'datasourceId': '240'}]
|
||||
2026-05-25 16:05:49 - mcp_services - INFO - [main.py:165] - 成功生成 1 个 MCP 工具
|
||||
2026-05-25 16:05:52 - mcp.server.lowlevel.server - INFO - [server.py:720] - Processing request of type CallToolRequest
|
||||
2026-05-25 16:05:52 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: qian50tiaodaorushujumingxichaxun_32b1d628
|
||||
2026-05-25 16:05:52 - mcp_services - INFO - [main.py:230] - 正在调用测试SQL API...
|
||||
2026-05-25 16:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:132] - ================================================================================
|
||||
2026-05-25 16:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:133] - test_sql_with_schema 接口接收到的数据:
|
||||
2026-05-25 16:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:134] - 数据类型: <class 'dict'>
|
||||
2026-05-25 16:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:135] - 数据内容: {
|
||||
"datasourceId": "240",
|
||||
"businessName": "qian50tiaodaorushujumingxichaxun_32b1d628",
|
||||
"businessDescription": "qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: 查询数据导入明细表中的前50条记录,包含原始数据、处理状态、错误原因等关键信息,用于快速预览最近的导入情况。",
|
||||
"sqlTemplate": "SELECT record_id AS \"记录ID\", task_id AS \"任务ID\", original_data AS \"原始数据\", process_result AS \"处理结果\", error_reason AS \"错误原因\", process_status AS \"处理状态\", target_table AS \"关联目标表\", created_time AS \"创建时间\" FROM import_record_detail LIMIT 50;",
|
||||
"parameters": {},
|
||||
"testParams": {}
|
||||
}
|
||||
2026-05-25 16:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:136] - 数据源ID: 240
|
||||
2026-05-25 16:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:137] - 业务名称: qian50tiaodaorushujumingxichaxun_32b1d628
|
||||
2026-05-25 16:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:138] - 业务描述: qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: 查询数据导入明细表中的前50条记录,包含原始数据、处理状态、错误原因等关键信息,用于快速预览最近的导入情况。
|
||||
2026-05-25 16:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:139] - SQL模板: SELECT record_id AS "记录ID", task_id AS "任务ID", original_data AS "原始数据", process_result AS "处理结果", error_reason AS "错误原因", process_status AS "处理状态", target_table AS "关联目标表", created_time AS "创建时间" FROM import_record_detail LIMIT 50;
|
||||
2026-05-25 16:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:140] - 参数定义: {}
|
||||
2026-05-25 16:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:141] - 测试参数: {}
|
||||
2026-05-25 16:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:144] - ================================================================================
|
||||
2026-05-25 16:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:153] - 正在调用测试SQL API: http://192.168.2.236:8088/datasource/sqlExecutionLog/testSqlWithSchema
|
||||
2026-05-25 16:05:52 - httpx - INFO - [_client.py:1025] - HTTP Request: POST http://192.168.2.236:8088/datasource/sqlExecutionLog/testSqlWithSchema "HTTP/1.1 200 "
|
||||
2026-05-25 16:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:170] - ================================================================================
|
||||
2026-05-25 16:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:171] - test_sql_with_schema 接口返回的数据:
|
||||
2026-05-25 16:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:172] - HTTP状态码: 200
|
||||
2026-05-25 16:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:173] - 响应数据类型: <class 'dict'>
|
||||
2026-05-25 16:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:174] - 响应数据内容: {
|
||||
"msg": "操作成功",
|
||||
"code": 200,
|
||||
"data": {
|
||||
"resultCount": 6,
|
||||
"data": [
|
||||
{
|
||||
"记录ID": 80001,
|
||||
"任务ID": "3320260418001",
|
||||
"原始数据": "{\"name\": \"张三\", \"mobile\": \"13800000101\", \"dept\": \"销售部\", \"amount\": 1200}",
|
||||
"处理结果": "导入成功",
|
||||
"错误原因": null,
|
||||
"处理状态": "SUCCESS",
|
||||
"关联目标表": "employee_import",
|
||||
"创建时间": "2026-04-18T15:19:19.000+08:00"
|
||||
},
|
||||
{
|
||||
"记录ID": 80002,
|
||||
"任务ID": "3320260418002",
|
||||
"原始数据": "{\"name\": \"李四\", \"mobile\": \"13800000102\", \"dept\": \"财务部\", \"amount\": 980}",
|
||||
"处理结果": "导入成功",
|
||||
"错误原因": null,
|
||||
"处理状态": "SUCCESS",
|
||||
"关联目标表": "employee_import",
|
||||
"创建时间": "2026-04-18T15:19:34.000+08:00"
|
||||
},
|
||||
{
|
||||
"记录ID": 80003,
|
||||
"任务ID": "3320260418003",
|
||||
"原始数据": "{\"name\": \"王五\", \"mobile\": \"13800000103\", \"dept\": \"技术部\", \"amount\": 1500}",
|
||||
"处理结果": "导入失败",
|
||||
"错误原因": "字段校验失败:mobile格式不正确",
|
||||
"处理状态": "FAILED",
|
||||
"关联目标表": "employee_import",
|
||||
"创建时间": "2026-04-18T15:19:49.000+08:00"
|
||||
},
|
||||
{
|
||||
"记录ID": 80004,
|
||||
"任务ID": "3320260418004",
|
||||
"原始数据": "{\"name\": \"赵六\", \"mobile\": \"13800000104\", \"dept\": \"人事部\", \"amount\": 0}",
|
||||
"处理结果": "导入成功",
|
||||
"错误原因": null,
|
||||
"处理状态": "SUCCESS",
|
||||
"关联目标表": "employee_import",
|
||||
"创建时间": "2026-04-18T15:20:04.000+08:00"
|
||||
},
|
||||
{
|
||||
"记录ID": 80005,
|
||||
"任务ID": "3320260418005",
|
||||
"原始数据": "{\"name\": \"孙七\", \"mobile\": \"13800000105\", \"dept\": \"运营部\", \"amount\": 2000}",
|
||||
"处理结果": "导入失败",
|
||||
"错误原因": "目标表写入失败:唯一键冲突",
|
||||
"处理状态": "FAILED",
|
||||
"关联目标表": "employee_import",
|
||||
"创建时间": "2026-04-18T15:20:19.000+08:00"
|
||||
},
|
||||
{
|
||||
"记录ID": 80006,
|
||||
"任务ID": "3320260418006",
|
||||
"原始数据": "{\"name\": \"周八\", \"mobile\": \"13800000106\", \"dept\": \"法务部\", \"amount\": 300}",
|
||||
"处理结果": "导入成功",
|
||||
"错误原因": null,
|
||||
"处理状态": "SUCCESS",
|
||||
"关联目标表": "employee_import",
|
||||
"创建时间": "2026-04-18T15:20:34.000+08:00"
|
||||
}
|
||||
],
|
||||
"databaseName": "import_log_db_168",
|
||||
"businessDescription": "qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: 查询数据导入明细表中的前50条记录,包含原始数据、处理状态、错误原因等关键信息,用于快速预览最近的导入情况。",
|
||||
"originalTemplate": "SELECT record_id AS \"记录ID\", task_id AS \"任务ID\", original_data AS \"原始数据\", process_result AS \"处理结果\", error_reason AS \"错误原因\", process_status AS \"处理状态\", target_table AS \"关联目标表\", created_time AS \"创建时间\" FROM import_record_detail LIMIT 50;",
|
||||
"convertedTemplate": "SELECT record_id AS \"记录ID\", task_id AS \"任务ID\", original_data AS \"原始数据\", process_result AS \"处理结果\", error_reason AS \"错误原因\", process_status AS \"处理状态\", target_table AS \"关联目标表\", created_time AS \"创建时间\" FROM import_record_detail LIMIT 50;",
|
||||
"executionStatus": "success",
|
||||
"businessName": "qian50tiaodaorushujumingxichaxun_32b1d628",
|
||||
"testParams": {},
|
||||
"errorMessage": null,
|
||||
"executionTime": 8,
|
||||
"datasourceId": "240",
|
||||
"logId": "2058821884796174336",
|
||||
"executableSql": "SELECT record_id AS \"记录ID\", task_id AS \"任务ID\", original_data AS \"原始数据\", process_result AS \"处理结果\", error_reason AS \"错误原因\", process_status AS \"处理状态\", target_table AS \"关联目标表\", created_time AS \"创建时间\" FROM import_record_detail LIMIT 50;",
|
||||
"datasourceName": "import_log_db_168"
|
||||
}
|
||||
}
|
||||
2026-05-25 16:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:176] - 响应code: 200
|
||||
2026-05-25 16:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:177] - 响应msg: 操作成功
|
||||
2026-05-25 16:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:178] - 响应data: {'resultCount': 6, 'data': [{'记录ID': 80001, '任务ID': '3320260418001', '原始数据': '{"name": "张三", "mobile": "13800000101", "dept": "销售部", "amount": 1200}', '处理结果': '导入成功', '错误原因': None, '处理状态': 'SUCCESS', '关联目标表': 'employee_import', '创建时间': '2026-04-18T15:19:19.000+08:00'}, {'记录ID': 80002, '任务ID': '3320260418002', '原始数据': '{"name": "李四", "mobile": "13800000102", "dept": "财务部", "amount": 980}', '处理结果': '导入成功', '错误原因': None, '处理状态': 'SUCCESS', '关联目标表': 'employee_import', '创建时间': '2026-04-18T15:19:34.000+08:00'}, {'记录ID': 80003, '任务ID': '3320260418003', '原始数据': '{"name": "王五", "mobile": "13800000103", "dept": "技术部", "amount": 1500}', '处理结果': '导入失败', '错误原因': '字段校验失败:mobile格式不正确', '处理状态': 'FAILED', '关联目标表': 'employee_import', '创建时间': '2026-04-18T15:19:49.000+08:00'}, {'记录ID': 80004, '任务ID': '3320260418004', '原始数据': '{"name": "赵六", "mobile": "13800000104", "dept": "人事部", "amount": 0}', '处理结果': '导入成功', '错误原因': None, '处理状态': 'SUCCESS', '关联目标表': 'employee_import', '创建时间': '2026-04-18T15:20:04.000+08:00'}, {'记录ID': 80005, '任务ID': '3320260418005', '原始数据': '{"name": "孙七", "mobile": "13800000105", "dept": "运营部", "amount": 2000}', '处理结果': '导入失败', '错误原因': '目标表写入失败:唯一键冲突', '处理状态': 'FAILED', '关联目标表': 'employee_import', '创建时间': '2026-04-18T15:20:19.000+08:00'}, {'记录ID': 80006, '任务ID': '3320260418006', '原始数据': '{"name": "周八", "mobile": "13800000106", "dept": "法务部", "amount": 300}', '处理结果': '导入成功', '错误原因': None, '处理状态': 'SUCCESS', '关联目标表': 'employee_import', '创建时间': '2026-04-18T15:20:34.000+08:00'}], 'databaseName': 'import_log_db_168', 'businessDescription': 'qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: 查询数据导入明细表中的前50条记录,包含原始数据、处理状态、错误原因等关键信息,用于快速预览最近的导入情况。', 'originalTemplate': 'SELECT record_id AS "记录ID", task_id AS "任务ID", original_data AS "原始数据", process_result AS "处理结果", error_reason AS "错误原因", process_status AS "处理状态", target_table AS "关联目标表", created_time AS "创建时间" FROM import_record_detail LIMIT 50;', 'convertedTemplate': 'SELECT record_id AS "记录ID", task_id AS "任务ID", original_data AS "原始数据", process_result AS "处理结果", error_reason AS "错误原因", process_status AS "处理状态", target_table AS "关联目标表", created_time AS "创建时间" FROM import_record_detail LIMIT 50;', 'executionStatus': 'success', 'businessName': 'qian50tiaodaorushujumingxichaxun_32b1d628', 'testParams': {}, 'errorMessage': None, 'executionTime': 8, 'datasourceId': '240', 'logId': '2058821884796174336', 'executableSql': 'SELECT record_id AS "记录ID", task_id AS "任务ID", original_data AS "原始数据", process_result AS "处理结果", error_reason AS "错误原因", process_status AS "处理状态", target_table AS "关联目标表", created_time AS "创建时间" FROM import_record_detail LIMIT 50;', 'datasourceName': 'import_log_db_168'}
|
||||
2026-05-25 16:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:179] - ================================================================================
|
||||
2026-05-25 16:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:181] - 测试SQL API调用成功
|
||||
2026-05-25 16:05:52 - mcp_services - INFO - [main.py:232] - 测试SQL API调用成功
|
||||
2026-05-25 16:15:08 - root - INFO - [logger_config.py:151] - 日志系统初始化完成 - 日志目录: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\logs
|
||||
2026-05-25 16:15:08 - root - INFO - [logger_config.py:152] - 日志配置 - 级别: INFO, 文件大小限制: 10MB, 备份数量: 5
|
||||
2026-05-25 16:15:08 - mcp_services - INFO - [main.py:246] - 开始运行 MCP SQL Executor 服务
|
||||
2026-05-25 16:15:08 - mcp_services - INFO - [main.py:204] - ============================================================
|
||||
2026-05-25 16:15:08 - mcp_services - INFO - [main.py:205] - 正在启动 MCP 服务: lzwcai-mcp-sqlexecutor
|
||||
2026-05-25 16:15:08 - mcp_services - INFO - [main.py:206] - 版本: 0.1.0
|
||||
2026-05-25 16:15:08 - mcp_services - INFO - [main.py:207] - ============================================================
|
||||
2026-05-25 16:15:08 - mcp_services - INFO - [main.py:210] - 环境配置 - Database ID: 240
|
||||
2026-05-25 16:15:08 - mcp_services - INFO - [main.py:211] - 环境配置 - Skill ID: 2058819964077572098
|
||||
2026-05-25 16:15:08 - mcp_services - INFO - [main.py:212] - 环境配置 - Backend Base URL: http://192.168.2.236:8088
|
||||
2026-05-25 16:15:08 - mcp_services - INFO - [main.py:213] - ============================================================
|
||||
2026-05-25 16:15:08 - mcp_services - INFO - [main.py:218] - MCP 服务已启动,等待客户端连接...
|
||||
2026-05-25 16:15:09 - mcp.server.lowlevel.server - INFO - [server.py:720] - Processing request of type ListToolsRequest
|
||||
2026-05-25 16:15:09 - mcp_services - INFO - [main.py:107] - 收到列出工具请求
|
||||
2026-05-25 16:15:09 - mcp_services - INFO - [main.py:77] - 初始化查询配置,数据源: api
|
||||
2026-05-25 16:15:09 - mcp_services - INFO - [main.py:189] - 调用第三方 API,skill_id: 2058819964077572098
|
||||
2026-05-25 16:15:09 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:71] - 正在调用API: http://192.168.2.236:8088/datasource/skill/getBySkillId/2058819964077572098
|
||||
2026-05-25 16:15:09 - httpx - INFO - [_client.py:1025] - HTTP Request: GET http://192.168.2.236:8088/datasource/skill/getBySkillId/2058819964077572098 "HTTP/1.1 200 "
|
||||
2026-05-25 16:15:09 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:85] - API调用成功: http://192.168.2.236:8088/datasource/skill/getBySkillId/2058819964077572098
|
||||
2026-05-25 16:15:09 - mcp_services - INFO - [main.py:191] - 成功获取原始响应: {'msg': '查询成功', 'code': 200, 'data': [{'id': '2058819964287287298', 'createBy': 'wxl06', 'createTime': '2026-05-25 15:58:25', 'updateBy': None, 'updateTime': None, 'serviceId': '2058819964085960705', 'uniqueName': '前50条导入数据明细查询', 'name': 'qian50tiaodaorushujumingxichaxun_32b1d628', 'description': 'qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: 查询数据导入明细表中的前50条记录,包含原始数据、处理状态、错误原因等关键信息,用于快速预览最近的导入情况。', 'visualizable': 1, 'toolPrompt': '查询成功,返回 6 行数据,执行时间: 1ms', 'toolType': 'sql', 'datasourceId': '240', 'sqlTemplate': 'SELECT record_id AS "记录ID", task_id AS "任务ID", original_data AS "原始数据", process_result AS "处理结果", error_reason AS "错误原因", process_status AS "处理状态", target_table AS "关联目标表", created_time AS "创建时间" FROM import_record_detail LIMIT 50;', 'sqlParams': '{}', 'resultType': 'list', 'sourceType': 'ai', 'trainingTaskId': None, 'tableMetadataIds': '', 'executionCount': 0, 'visualizationConfigs': None, 'inputJsonSchema': '{"type":"object","properties":{},"required":[]}', 'outputJsonSchema': '{"type":"object","properties":{"text":{"type":"string"}},"additionalProperties":false}', 'lastExecutionTime': None}]}
|
||||
2026-05-25 16:15:09 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:325] - 成功处理 1 条技能数据
|
||||
2026-05-25 16:15:09 - mcp_services - INFO - [main.py:194] - 成功处理 1 条数据
|
||||
2026-05-25 16:15:09 - mcp_services - INFO - [main.py:85] - API 配置: 1 条
|
||||
2026-05-25 16:15:09 - mcp_services - INFO - [main.py:86] - API 配置数组: [{'id': '2058819964287287298', 'businessName': 'qian50tiaodaorushujumingxichaxun_32b1d628', 'businessDescription': 'qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: 查询数据导入明细表中的前50条记录,包含原始数据、处理状态、错误原因等关键信息,用于快速预览最近的导入情况。', 'sqlTemplate': 'SELECT record_id AS "记录ID", task_id AS "任务ID", original_data AS "原始数据", process_result AS "处理结果", error_reason AS "错误原因", process_status AS "处理状态", target_table AS "关联目标表", created_time AS "创建时间" FROM import_record_detail LIMIT 50;', 'parameters': {}, 'datasourceId': '240'}]
|
||||
2026-05-25 16:15:09 - mcp_services - INFO - [main.py:110] - 成功生成 1 个 MCP 工具
|
||||
2026-05-25 16:22:48 - root - INFO - [logger_config.py:151] - 日志系统初始化完成 - 日志目录: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\logs
|
||||
2026-05-25 16:22:48 - root - INFO - [logger_config.py:152] - 日志配置 - 级别: INFO, 文件大小限制: 10MB, 备份数量: 5
|
||||
2026-05-25 16:22:48 - mcp_services - INFO - [main.py:258] - 开始运行 MCP SQL Executor 服务
|
||||
2026-05-25 16:22:48 - mcp_services - INFO - [main.py:216] - ============================================================
|
||||
2026-05-25 16:22:48 - mcp_services - INFO - [main.py:217] - 正在启动 MCP 服务: lzwcai-mcp-sqlexecutor
|
||||
2026-05-25 16:22:48 - mcp_services - INFO - [main.py:218] - 版本: 0.1.0
|
||||
2026-05-25 16:22:48 - mcp_services - INFO - [main.py:219] - ============================================================
|
||||
2026-05-25 16:22:48 - mcp_services - INFO - [main.py:222] - 环境配置 - Database ID: 29
|
||||
2026-05-25 16:22:48 - mcp_services - INFO - [main.py:223] - 环境配置 - Skill ID:
|
||||
2026-05-25 16:22:48 - mcp_services - INFO - [main.py:224] - 环境配置 - Backend Base URL: http://lzwcai-demp-corp-manager:8086
|
||||
2026-05-25 16:22:48 - mcp_services - INFO - [main.py:225] - ============================================================
|
||||
2026-05-25 16:22:48 - mcp_services - INFO - [main.py:230] - MCP 服务已启动,等待客户端连接...
|
||||
2026-05-25 16:22:51 - mcp.server.lowlevel.server - INFO - [server.py:720] - Processing request of type ListToolsRequest
|
||||
2026-05-25 16:22:51 - mcp_services - INFO - [main.py:127] - 收到列出工具请求
|
||||
2026-05-25 16:22:51 - mcp_services - INFO - [main.py:97] - 初始化查询配置,数据源: api
|
||||
2026-05-25 16:22:51 - mcp_services - INFO - [main.py:201] - 调用第三方 API,skill_id:
|
||||
2026-05-25 16:22:51 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:47] - 正在调用 API: http://lzwcai-demp-corp-manager:8086/datasource/skill/getBySkillId/
|
||||
2026-05-25 16:22:53 - lzwcai_mcp_sqlexecutor.utils.api_client - ERROR - [api_client.py:69] - API 请求异常: http://lzwcai-demp-corp-manager:8086/datasource/skill/getBySkillId/, 错误: [Errno 11001] getaddrinfo failed
|
||||
2026-05-25 16:22:53 - mcp_services - ERROR - [main.py:209] - API 调用失败: API 请求异常: http://lzwcai-demp-corp-manager:8086/datasource/skill/getBySkillId/, 错误: [Errno 11001] getaddrinfo failed
|
||||
Traceback (most recent call last):
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_transports\default.py", line 101, in map_httpcore_exceptions
|
||||
yield
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_transports\default.py", line 250, in handle_request
|
||||
resp = self._pool.handle_request(req)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection_pool.py", line 256, in handle_request
|
||||
raise exc from None
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection_pool.py", line 236, in handle_request
|
||||
response = connection.handle_request(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection.py", line 101, in handle_request
|
||||
raise exc
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection.py", line 78, in handle_request
|
||||
stream = self._connect(request)
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection.py", line 124, in _connect
|
||||
stream = self._network_backend.connect_tcp(**kwargs)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_backends\sync.py", line 207, in connect_tcp
|
||||
with map_exceptions(exc_map):
|
||||
File "D:\anaconda3\Lib\contextlib.py", line 158, in __exit__
|
||||
self.gen.throw(value)
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_exceptions.py", line 14, in map_exceptions
|
||||
raise to_exc(exc) from exc
|
||||
httpcore.ConnectError: [Errno 11001] getaddrinfo failed
|
||||
|
||||
The above exception was the direct cause of the following exception:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 50, in get_skill_by_id
|
||||
response = self.client.get(url, headers=self._get_headers())
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_client.py", line 1053, in get
|
||||
return self.request(
|
||||
^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_client.py", line 825, in request
|
||||
return self.send(request, auth=auth, follow_redirects=follow_redirects)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_client.py", line 914, in send
|
||||
response = self._send_handling_auth(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_client.py", line 942, in _send_handling_auth
|
||||
response = self._send_handling_redirects(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_client.py", line 979, in _send_handling_redirects
|
||||
response = self._send_single_request(request)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_client.py", line 1014, in _send_single_request
|
||||
response = transport.handle_request(request)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_transports\default.py", line 249, in handle_request
|
||||
with map_httpcore_exceptions():
|
||||
File "D:\anaconda3\Lib\contextlib.py", line 158, in __exit__
|
||||
self.gen.throw(value)
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_transports\default.py", line 118, in map_httpcore_exceptions
|
||||
raise mapped_exc(message) from exc
|
||||
httpx.ConnectError: [Errno 11001] getaddrinfo failed
|
||||
|
||||
During handling of the above exception, another exception occurred:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 202, in call_third_party_api
|
||||
raw_result = get_skill_by_id(skill_id)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 154, in get_skill_by_id
|
||||
return default_client.get_skill_by_id(skill_id)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 70, in get_skill_by_id
|
||||
raise Exception(error_msg)
|
||||
Exception: API 请求异常: http://lzwcai-demp-corp-manager:8086/datasource/skill/getBySkillId/, 错误: [Errno 11001] getaddrinfo failed
|
||||
2026-05-25 16:22:53 - mcp_services - WARNING - [main.py:108] - API 获取失败,降级使用本地配置: API 请求异常: http://lzwcai-demp-corp-manager:8086/datasource/skill/getBySkillId/, 错误: [Errno 11001] getaddrinfo failed
|
||||
2026-05-25 16:22:53 - mcp_services - INFO - [main.py:58] - 成功加载 0 个业务查询配置
|
||||
2026-05-25 16:22:53 - mcp_services - INFO - [main.py:130] - 成功生成 0 个 MCP 工具
|
||||
2026-05-25 16:22:53 - mcp.server.lowlevel.server - INFO - [server.py:720] - Processing request of type ListToolsRequest
|
||||
2026-05-25 16:22:53 - mcp_services - INFO - [main.py:127] - 收到列出工具请求
|
||||
2026-05-25 16:22:53 - mcp_services - INFO - [main.py:130] - 成功生成 0 个 MCP 工具
|
||||
2026-05-25 16:23:09 - mcp.server.lowlevel.server - INFO - [server.py:720] - Processing request of type ListToolsRequest
|
||||
2026-05-25 16:23:09 - mcp_services - INFO - [main.py:127] - 收到列出工具请求
|
||||
2026-05-25 16:23:09 - mcp_services - INFO - [main.py:130] - 成功生成 0 个 MCP 工具
|
||||
2026-05-25 16:26:37 - root - INFO - [logger_config.py:151] - 日志系统初始化完成 - 日志目录: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\logs
|
||||
2026-05-25 16:26:37 - root - INFO - [logger_config.py:152] - 日志配置 - 级别: INFO, 文件大小限制: 10MB, 备份数量: 5
|
||||
2026-05-25 16:26:37 - mcp_services - INFO - [main.py:258] - 开始运行 MCP SQL Executor 服务
|
||||
2026-05-25 16:26:37 - mcp_services - INFO - [main.py:216] - ============================================================
|
||||
2026-05-25 16:26:37 - mcp_services - INFO - [main.py:217] - 正在启动 MCP 服务: lzwcai-mcp-sqlexecutor
|
||||
2026-05-25 16:26:37 - mcp_services - INFO - [main.py:218] - 版本: 0.1.0
|
||||
2026-05-25 16:26:37 - mcp_services - INFO - [main.py:219] - ============================================================
|
||||
2026-05-25 16:26:37 - mcp_services - INFO - [main.py:222] - 环境配置 - Database ID: 240
|
||||
2026-05-25 16:26:37 - mcp_services - INFO - [main.py:223] - 环境配置 - Skill ID: 2058819964077572098
|
||||
2026-05-25 16:26:37 - mcp_services - INFO - [main.py:224] - 环境配置 - Backend Base URL: http://192.168.2.236:8088
|
||||
2026-05-25 16:26:37 - mcp_services - INFO - [main.py:225] - ============================================================
|
||||
2026-05-25 16:26:37 - mcp_services - INFO - [main.py:230] - MCP 服务已启动,等待客户端连接...
|
||||
2026-05-25 16:26:38 - mcp.server.lowlevel.server - INFO - [server.py:720] - Processing request of type ListToolsRequest
|
||||
2026-05-25 16:26:38 - mcp_services - INFO - [main.py:127] - 收到列出工具请求
|
||||
2026-05-25 16:26:38 - mcp_services - INFO - [main.py:97] - 初始化查询配置,数据源: api
|
||||
2026-05-25 16:26:38 - mcp_services - INFO - [main.py:201] - 调用第三方 API,skill_id: 2058819964077572098
|
||||
2026-05-25 16:26:38 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:47] - 正在调用 API: http://192.168.2.236:8088/datasource/skill/getBySkillId/2058819964077572098
|
||||
2026-05-25 16:26:38 - httpx - INFO - [_client.py:1025] - HTTP Request: GET http://192.168.2.236:8088/datasource/skill/getBySkillId/2058819964077572098 "HTTP/1.1 200 "
|
||||
2026-05-25 16:26:38 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:54] - API 调用成功: http://192.168.2.236:8088/datasource/skill/getBySkillId/2058819964077572098
|
||||
2026-05-25 16:26:38 - mcp_services - INFO - [main.py:203] - 成功获取原始响应: {'msg': '查询成功', 'code': 200, 'data': [{'id': '2058819964287287298', 'createBy': 'wxl06', 'createTime': '2026-05-25 15:58:25', 'updateBy': None, 'updateTime': None, 'serviceId': '2058819964085960705', 'uniqueName': '前50条导入数据明细查询', 'name': 'qian50tiaodaorushujumingxichaxun_32b1d628', 'description': 'qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: 查询数据导入明细表中的前50条记录,包含原始数据、处理状态、错误原因等关键信息,用于快速预览最近的导入情况。', 'visualizable': 1, 'toolPrompt': '查询成功,返回 6 行数据,执行时间: 1ms', 'toolType': 'sql', 'datasourceId': '240', 'sqlTemplate': 'SELECT record_id AS "记录ID", task_id AS "任务ID", original_data AS "原始数据", process_result AS "处理结果", error_reason AS "错误原因", process_status AS "处理状态", target_table AS "关联目标表", created_time AS "创建时间" FROM import_record_detail LIMIT 50;', 'sqlParams': '{}', 'resultType': 'list', 'sourceType': 'ai', 'trainingTaskId': None, 'tableMetadataIds': '', 'executionCount': 0, 'visualizationConfigs': None, 'inputJsonSchema': '{"type":"object","properties":{},"required":[]}', 'outputJsonSchema': '{"type":"object","properties":{"text":{"type":"string"}},"additionalProperties":false}', 'lastExecutionTime': None}]}
|
||||
2026-05-25 16:26:38 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:205] - 成功处理 1 条技能数据
|
||||
2026-05-25 16:26:38 - mcp_services - INFO - [main.py:206] - 成功处理 1 条数据
|
||||
2026-05-25 16:26:38 - mcp_services - INFO - [main.py:105] - API 配置: 1 条
|
||||
2026-05-25 16:26:38 - mcp_services - INFO - [main.py:106] - API 配置数组: [{'id': '2058819964287287298', 'businessName': 'qian50tiaodaorushujumingxichaxun_32b1d628', 'businessDescription': 'qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: qian50tiaodaorushujumingxichaxun_32b1d628: 查询数据导入明细表中的前50条记录,包含原始数据、处理状态、错误原因等关键信息,用于快速预览最近的导入情况。', 'sqlTemplate': 'SELECT record_id AS "记录ID", task_id AS "任务ID", original_data AS "原始数据", process_result AS "处理结果", error_reason AS "错误原因", process_status AS "处理状态", target_table AS "关联目标表", created_time AS "创建时间" FROM import_record_detail LIMIT 50;', 'parameters': {}, 'datasourceId': '240'}]
|
||||
2026-05-25 16:26:38 - mcp_services - INFO - [main.py:130] - 成功生成 1 个 MCP 工具
|
||||
|
||||
@@ -1,223 +0,0 @@
|
||||
2025-12-31 12:57:27 - root - INFO - [logger_config.py:151] - 日志系统初始化完成 - 日志目录: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\logs
|
||||
2025-12-31 12:57:27 - root - INFO - [logger_config.py:152] - 日志配置 - 级别: INFO, 文件大小限制: 10MB, 备份数量: 5
|
||||
2025-12-31 12:57:27 - mcp_services - INFO - [main.py:347] - 开始运行 MCP SQL Executor 服务器
|
||||
2025-12-31 12:57:27 - mcp_services - INFO - [main.py:299] - ============================================================
|
||||
2025-12-31 12:57:27 - mcp_services - INFO - [main.py:300] - 正在启动 MCP 服务器: lzwcai-mcp-sqlexecutor
|
||||
2025-12-31 12:57:27 - mcp_services - INFO - [main.py:301] - 版本: 0.1.0
|
||||
2025-12-31 12:57:27 - mcp_services - INFO - [main.py:302] - ============================================================
|
||||
2025-12-31 12:57:27 - mcp_services - INFO - [main.py:306] - 环境配置 - Database ID: 16
|
||||
2025-12-31 12:57:27 - mcp_services - INFO - [main.py:307] - 环境配置 - Skill ID:
|
||||
2025-12-31 12:57:27 - mcp_services - INFO - [main.py:308] - 环境配置 - Backend Base URL: http://192.168.11.24:8088
|
||||
2025-12-31 12:57:27 - mcp_services - INFO - [main.py:309] - ============================================================
|
||||
2025-12-31 12:57:27 - mcp_services - INFO - [main.py:314] - MCP 服务器已启动,等待客户端连接...
|
||||
2025-12-31 12:57:28 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type ListToolsRequest
|
||||
2025-12-31 12:57:28 - mcp_services - INFO - [main.py:156] - 收到列出工具请求
|
||||
2025-12-31 12:57:28 - mcp_services - INFO - [main.py:119] - 初始化查询配置(数据源: api)...
|
||||
2025-12-31 12:57:28 - mcp_services - INFO - [main.py:278] - 调用第三方API,skill_id:
|
||||
2025-12-31 12:57:28 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:71] - 正在调用API: http://192.168.11.24:8088/datasource/skill/getBySkillId/
|
||||
2025-12-31 12:57:29 - httpx - INFO - [_client.py:1025] - HTTP Request: GET http://192.168.11.24:8088/datasource/skill/getBySkillId/ "HTTP/1.1 404 "
|
||||
2025-12-31 12:57:29 - lzwcai_mcp_sqlexecutor.utils.api_client - ERROR - [api_client.py:97] - API请求失败 (HTTP 404): http://192.168.11.24:8088/datasource/skill/getBySkillId/
|
||||
2025-12-31 12:57:29 - lzwcai_mcp_sqlexecutor.utils.api_client - ERROR - [api_client.py:98] - 错误响应: {"timestamp":"2025-12-31T12:57:30.248+08:00","status":404,"error":"Not Found","path":"/datasource/skill/getBySkillId/"}
|
||||
2025-12-31 12:57:29 - mcp_services - ERROR - [main.py:292] - API调用失败: API请求失败 (HTTP 404): http://192.168.11.24:8088/datasource/skill/getBySkillId/
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 80, in get_skill_by_id
|
||||
response.raise_for_status()
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_models.py", line 829, in raise_for_status
|
||||
raise HTTPStatusError(message, request=request, response=self)
|
||||
httpx.HTTPStatusError: Client error '404 ' for url 'http://192.168.11.24:8088/datasource/skill/getBySkillId/'
|
||||
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404
|
||||
|
||||
During handling of the above exception, another exception occurred:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 281, in call_third_party_api
|
||||
raw_result = get_skill_by_id(skill_id)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 208, in get_skill_by_id
|
||||
return default_client.get_skill_by_id(skill_id)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 99, in get_skill_by_id
|
||||
raise Exception(error_msg)
|
||||
Exception: API请求失败 (HTTP 404): http://192.168.11.24:8088/datasource/skill/getBySkillId/
|
||||
2025-12-31 12:57:29 - mcp_services - WARNING - [main.py:131] - API获取失败,降级使用本地配置: API请求失败 (HTTP 404): http://192.168.11.24:8088/datasource/skill/getBySkillId/
|
||||
2025-12-31 12:57:29 - mcp_services - INFO - [main.py:55] - 成功加载 3 个业务查询配置
|
||||
2025-12-31 12:57:29 - mcp_services - ERROR - [main.py:92] - 生成工具模式失败: 2006300000000000001, 错误: 'businessName'
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
|
||||
tool_name = query['businessName']
|
||||
~~~~~^^^^^^^^^^^^^^^^
|
||||
KeyError: 'businessName'
|
||||
2025-12-31 12:57:29 - mcp_services - ERROR - [main.py:170] - 列出工具失败: 'businessName'
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 162, in handle_list_tools
|
||||
tool = generate_tool_schema_from_query(query)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
|
||||
tool_name = query['businessName']
|
||||
~~~~~^^^^^^^^^^^^^^^^
|
||||
KeyError: 'businessName'
|
||||
2025-12-31 12:57:29 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type ListToolsRequest
|
||||
2025-12-31 12:57:29 - mcp_services - INFO - [main.py:156] - 收到列出工具请求
|
||||
2025-12-31 12:57:29 - mcp_services - ERROR - [main.py:92] - 生成工具模式失败: 2006300000000000001, 错误: 'businessName'
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
|
||||
tool_name = query['businessName']
|
||||
~~~~~^^^^^^^^^^^^^^^^
|
||||
KeyError: 'businessName'
|
||||
2025-12-31 12:57:29 - mcp_services - ERROR - [main.py:170] - 列出工具失败: 'businessName'
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 162, in handle_list_tools
|
||||
tool = generate_tool_schema_from_query(query)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
|
||||
tool_name = query['businessName']
|
||||
~~~~~^^^^^^^^^^^^^^^^
|
||||
KeyError: 'businessName'
|
||||
2025-12-31 12:57:54 - root - INFO - [logger_config.py:151] - 日志系统初始化完成 - 日志目录: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\logs
|
||||
2025-12-31 12:57:54 - root - INFO - [logger_config.py:152] - 日志配置 - 级别: INFO, 文件大小限制: 10MB, 备份数量: 5
|
||||
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:347] - 开始运行 MCP SQL Executor 服务器
|
||||
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:299] - ============================================================
|
||||
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:300] - 正在启动 MCP 服务器: lzwcai-mcp-sqlexecutor
|
||||
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:301] - 版本: 0.1.0
|
||||
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:302] - ============================================================
|
||||
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:306] - 环境配置 - Database ID: 16
|
||||
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:307] - 环境配置 - Skill ID:
|
||||
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:308] - 环境配置 - Backend Base URL: http://192.168.11.24:8088
|
||||
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:309] - ============================================================
|
||||
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:314] - MCP 服务器已启动,等待客户端连接...
|
||||
2025-12-31 12:57:54 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type ListToolsRequest
|
||||
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:156] - 收到列出工具请求
|
||||
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:119] - 初始化查询配置(数据源: local)...
|
||||
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:55] - 成功加载 3 个业务查询配置
|
||||
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:123] - 本地配置: 3 条
|
||||
2025-12-31 12:57:54 - mcp_services - ERROR - [main.py:92] - 生成工具模式失败: 2006300000000000001, 错误: 'businessName'
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
|
||||
tool_name = query['businessName']
|
||||
~~~~~^^^^^^^^^^^^^^^^
|
||||
KeyError: 'businessName'
|
||||
2025-12-31 12:57:54 - mcp_services - ERROR - [main.py:170] - 列出工具失败: 'businessName'
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 162, in handle_list_tools
|
||||
tool = generate_tool_schema_from_query(query)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
|
||||
tool_name = query['businessName']
|
||||
~~~~~^^^^^^^^^^^^^^^^
|
||||
KeyError: 'businessName'
|
||||
2025-12-31 15:00:31 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type ListToolsRequest
|
||||
2025-12-31 15:00:31 - mcp_services - INFO - [main.py:156] - 收到列出工具请求
|
||||
2025-12-31 15:00:31 - mcp_services - ERROR - [main.py:92] - 生成工具模式失败: 2006300000000000001, 错误: 'businessName'
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
|
||||
tool_name = query['businessName']
|
||||
~~~~~^^^^^^^^^^^^^^^^
|
||||
KeyError: 'businessName'
|
||||
2025-12-31 15:00:31 - mcp_services - ERROR - [main.py:170] - 列出工具失败: 'businessName'
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 162, in handle_list_tools
|
||||
tool = generate_tool_schema_from_query(query)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
|
||||
tool_name = query['businessName']
|
||||
~~~~~^^^^^^^^^^^^^^^^
|
||||
KeyError: 'businessName'
|
||||
2025-12-31 15:00:34 - mcp_services - INFO - [main.py:329] - MCP 服务器已关闭
|
||||
2025-12-31 15:00:53 - root - INFO - [logger_config.py:151] - 日志系统初始化完成 - 日志目录: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\logs
|
||||
2025-12-31 15:00:53 - root - INFO - [logger_config.py:152] - 日志配置 - 级别: INFO, 文件大小限制: 10MB, 备份数量: 5
|
||||
2025-12-31 15:00:53 - mcp_services - INFO - [main.py:347] - 开始运行 MCP SQL Executor 服务器
|
||||
2025-12-31 15:00:53 - mcp_services - INFO - [main.py:299] - ============================================================
|
||||
2025-12-31 15:00:53 - mcp_services - INFO - [main.py:300] - 正在启动 MCP 服务器: lzwcai-mcp-sqlexecutor
|
||||
2025-12-31 15:00:53 - mcp_services - INFO - [main.py:301] - 版本: 0.1.0
|
||||
2025-12-31 15:00:53 - mcp_services - INFO - [main.py:302] - ============================================================
|
||||
2025-12-31 15:00:53 - mcp_services - INFO - [main.py:306] - 环境配置 - Database ID: 29
|
||||
2025-12-31 15:00:53 - mcp_services - INFO - [main.py:307] - 环境配置 - Skill ID:
|
||||
2025-12-31 15:00:53 - mcp_services - INFO - [main.py:308] - 环境配置 - Backend Base URL: http://lzwcai-demp-corp-manager:8086
|
||||
2025-12-31 15:00:53 - mcp_services - INFO - [main.py:309] - ============================================================
|
||||
2025-12-31 15:00:53 - mcp_services - INFO - [main.py:314] - MCP 服务器已启动,等待客户端连接...
|
||||
2025-12-31 15:00:54 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type ListToolsRequest
|
||||
2025-12-31 15:00:54 - mcp_services - INFO - [main.py:156] - 收到列出工具请求
|
||||
2025-12-31 15:00:54 - mcp_services - INFO - [main.py:119] - 初始化查询配置(数据源: api)...
|
||||
2025-12-31 15:00:54 - mcp_services - INFO - [main.py:278] - 调用第三方API,skill_id:
|
||||
2025-12-31 15:00:54 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:71] - 正在调用API: http://lzwcai-demp-corp-manager:8086/datasource/skill/getBySkillId/
|
||||
2025-12-31 15:00:56 - lzwcai_mcp_sqlexecutor.utils.api_client - ERROR - [api_client.py:103] - API请求异常: http://lzwcai-demp-corp-manager:8086/datasource/skill/getBySkillId/, 错误: [Errno 11001] getaddrinfo failed
|
||||
2025-12-31 15:00:56 - mcp_services - ERROR - [main.py:292] - API调用失败: API请求异常: http://lzwcai-demp-corp-manager:8086/datasource/skill/getBySkillId/, 错误: [Errno 11001] getaddrinfo failed
|
||||
Traceback (most recent call last):
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_transports\default.py", line 101, in map_httpcore_exceptions
|
||||
yield
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_transports\default.py", line 250, in handle_request
|
||||
resp = self._pool.handle_request(req)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection_pool.py", line 216, in handle_request
|
||||
raise exc from None
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection_pool.py", line 196, in handle_request
|
||||
response = connection.handle_request(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection.py", line 99, in handle_request
|
||||
raise exc
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection.py", line 76, in handle_request
|
||||
stream = self._connect(request)
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection.py", line 122, in _connect
|
||||
stream = self._network_backend.connect_tcp(**kwargs)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_backends\sync.py", line 205, in connect_tcp
|
||||
with map_exceptions(exc_map):
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\contextlib.py", line 158, in __exit__
|
||||
self.gen.throw(value)
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_exceptions.py", line 14, in map_exceptions
|
||||
raise to_exc(exc) from exc
|
||||
httpcore.ConnectError: [Errno 11001] getaddrinfo failed
|
||||
|
||||
The above exception was the direct cause of the following exception:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 74, in get_skill_by_id
|
||||
response = self.client.get(
|
||||
^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_client.py", line 1053, in get
|
||||
return self.request(
|
||||
^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_client.py", line 825, in request
|
||||
return self.send(request, auth=auth, follow_redirects=follow_redirects)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_client.py", line 914, in send
|
||||
response = self._send_handling_auth(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_client.py", line 942, in _send_handling_auth
|
||||
response = self._send_handling_redirects(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_client.py", line 979, in _send_handling_redirects
|
||||
response = self._send_single_request(request)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_client.py", line 1014, in _send_single_request
|
||||
response = transport.handle_request(request)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_transports\default.py", line 249, in handle_request
|
||||
with map_httpcore_exceptions():
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\contextlib.py", line 158, in __exit__
|
||||
self.gen.throw(value)
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_transports\default.py", line 118, in map_httpcore_exceptions
|
||||
raise mapped_exc(message) from exc
|
||||
httpx.ConnectError: [Errno 11001] getaddrinfo failed
|
||||
|
||||
During handling of the above exception, another exception occurred:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 281, in call_third_party_api
|
||||
raw_result = get_skill_by_id(skill_id)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 208, in get_skill_by_id
|
||||
return default_client.get_skill_by_id(skill_id)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 104, in get_skill_by_id
|
||||
raise Exception(error_msg)
|
||||
Exception: API请求异常: http://lzwcai-demp-corp-manager:8086/datasource/skill/getBySkillId/, 错误: [Errno 11001] getaddrinfo failed
|
||||
2025-12-31 15:00:56 - mcp_services - WARNING - [main.py:131] - API获取失败,降级使用本地配置: API请求异常: http://lzwcai-demp-corp-manager:8086/datasource/skill/getBySkillId/, 错误: [Errno 11001] getaddrinfo failed
|
||||
2025-12-31 15:00:56 - mcp_services - INFO - [main.py:55] - 成功加载 0 个业务查询配置
|
||||
2025-12-31 15:00:56 - mcp_services - INFO - [main.py:165] - 成功生成 0 个 MCP 工具
|
||||
2025-12-31 15:00:56 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type ListToolsRequest
|
||||
2025-12-31 15:00:56 - mcp_services - INFO - [main.py:156] - 收到列出工具请求
|
||||
2025-12-31 15:00:56 - mcp_services - INFO - [main.py:165] - 成功生成 0 个 MCP 工具
|
||||
File diff suppressed because one or more lines are too long
@@ -1,110 +1,26 @@
|
||||
2025-12-31 12:57:29 - lzwcai_mcp_sqlexecutor.utils.api_client - ERROR - [api_client.py:97] - API请求失败 (HTTP 404): http://192.168.11.24:8088/datasource/skill/getBySkillId/
|
||||
2025-12-31 12:57:29 - lzwcai_mcp_sqlexecutor.utils.api_client - ERROR - [api_client.py:98] - 错误响应: {"timestamp":"2025-12-31T12:57:30.248+08:00","status":404,"error":"Not Found","path":"/datasource/skill/getBySkillId/"}
|
||||
2025-12-31 12:57:29 - mcp_services - ERROR - [main.py:292] - API调用失败: API请求失败 (HTTP 404): http://192.168.11.24:8088/datasource/skill/getBySkillId/
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 80, in get_skill_by_id
|
||||
response.raise_for_status()
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_models.py", line 829, in raise_for_status
|
||||
raise HTTPStatusError(message, request=request, response=self)
|
||||
httpx.HTTPStatusError: Client error '404 ' for url 'http://192.168.11.24:8088/datasource/skill/getBySkillId/'
|
||||
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404
|
||||
|
||||
During handling of the above exception, another exception occurred:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 281, in call_third_party_api
|
||||
raw_result = get_skill_by_id(skill_id)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 208, in get_skill_by_id
|
||||
return default_client.get_skill_by_id(skill_id)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 99, in get_skill_by_id
|
||||
raise Exception(error_msg)
|
||||
Exception: API请求失败 (HTTP 404): http://192.168.11.24:8088/datasource/skill/getBySkillId/
|
||||
2025-12-31 12:57:29 - mcp_services - ERROR - [main.py:92] - 生成工具模式失败: 2006300000000000001, 错误: 'businessName'
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
|
||||
tool_name = query['businessName']
|
||||
~~~~~^^^^^^^^^^^^^^^^
|
||||
KeyError: 'businessName'
|
||||
2025-12-31 12:57:29 - mcp_services - ERROR - [main.py:170] - 列出工具失败: 'businessName'
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 162, in handle_list_tools
|
||||
tool = generate_tool_schema_from_query(query)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
|
||||
tool_name = query['businessName']
|
||||
~~~~~^^^^^^^^^^^^^^^^
|
||||
KeyError: 'businessName'
|
||||
2025-12-31 12:57:29 - mcp_services - ERROR - [main.py:92] - 生成工具模式失败: 2006300000000000001, 错误: 'businessName'
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
|
||||
tool_name = query['businessName']
|
||||
~~~~~^^^^^^^^^^^^^^^^
|
||||
KeyError: 'businessName'
|
||||
2025-12-31 12:57:29 - mcp_services - ERROR - [main.py:170] - 列出工具失败: 'businessName'
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 162, in handle_list_tools
|
||||
tool = generate_tool_schema_from_query(query)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
|
||||
tool_name = query['businessName']
|
||||
~~~~~^^^^^^^^^^^^^^^^
|
||||
KeyError: 'businessName'
|
||||
2025-12-31 12:57:54 - mcp_services - ERROR - [main.py:92] - 生成工具模式失败: 2006300000000000001, 错误: 'businessName'
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
|
||||
tool_name = query['businessName']
|
||||
~~~~~^^^^^^^^^^^^^^^^
|
||||
KeyError: 'businessName'
|
||||
2025-12-31 12:57:54 - mcp_services - ERROR - [main.py:170] - 列出工具失败: 'businessName'
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 162, in handle_list_tools
|
||||
tool = generate_tool_schema_from_query(query)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
|
||||
tool_name = query['businessName']
|
||||
~~~~~^^^^^^^^^^^^^^^^
|
||||
KeyError: 'businessName'
|
||||
2025-12-31 15:00:31 - mcp_services - ERROR - [main.py:92] - 生成工具模式失败: 2006300000000000001, 错误: 'businessName'
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
|
||||
tool_name = query['businessName']
|
||||
~~~~~^^^^^^^^^^^^^^^^
|
||||
KeyError: 'businessName'
|
||||
2025-12-31 15:00:31 - mcp_services - ERROR - [main.py:170] - 列出工具失败: 'businessName'
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 162, in handle_list_tools
|
||||
tool = generate_tool_schema_from_query(query)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
|
||||
tool_name = query['businessName']
|
||||
~~~~~^^^^^^^^^^^^^^^^
|
||||
KeyError: 'businessName'
|
||||
2025-12-31 15:00:56 - lzwcai_mcp_sqlexecutor.utils.api_client - ERROR - [api_client.py:103] - API请求异常: http://lzwcai-demp-corp-manager:8086/datasource/skill/getBySkillId/, 错误: [Errno 11001] getaddrinfo failed
|
||||
2025-12-31 15:00:56 - mcp_services - ERROR - [main.py:292] - API调用失败: API请求异常: http://lzwcai-demp-corp-manager:8086/datasource/skill/getBySkillId/, 错误: [Errno 11001] getaddrinfo failed
|
||||
2026-05-25 16:22:53 - lzwcai_mcp_sqlexecutor.utils.api_client - ERROR - [api_client.py:69] - API 请求异常: http://lzwcai-demp-corp-manager:8086/datasource/skill/getBySkillId/, 错误: [Errno 11001] getaddrinfo failed
|
||||
2026-05-25 16:22:53 - mcp_services - ERROR - [main.py:209] - API 调用失败: API 请求异常: http://lzwcai-demp-corp-manager:8086/datasource/skill/getBySkillId/, 错误: [Errno 11001] getaddrinfo failed
|
||||
Traceback (most recent call last):
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_transports\default.py", line 101, in map_httpcore_exceptions
|
||||
yield
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_transports\default.py", line 250, in handle_request
|
||||
resp = self._pool.handle_request(req)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection_pool.py", line 216, in handle_request
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection_pool.py", line 256, in handle_request
|
||||
raise exc from None
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection_pool.py", line 196, in handle_request
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection_pool.py", line 236, in handle_request
|
||||
response = connection.handle_request(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection.py", line 99, in handle_request
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection.py", line 101, in handle_request
|
||||
raise exc
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection.py", line 76, in handle_request
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection.py", line 78, in handle_request
|
||||
stream = self._connect(request)
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection.py", line 122, in _connect
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection.py", line 124, in _connect
|
||||
stream = self._network_backend.connect_tcp(**kwargs)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_backends\sync.py", line 205, in connect_tcp
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_backends\sync.py", line 207, in connect_tcp
|
||||
with map_exceptions(exc_map):
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\contextlib.py", line 158, in __exit__
|
||||
self.gen.throw(value)
|
||||
File "D:\anaconda3\Lib\site-packages\httpcore\_exceptions.py", line 14, in map_exceptions
|
||||
@@ -114,9 +30,9 @@ httpcore.ConnectError: [Errno 11001] getaddrinfo failed
|
||||
The above exception was the direct cause of the following exception:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 74, in get_skill_by_id
|
||||
response = self.client.get(
|
||||
^^^^^^^^^^^^^^^^
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 50, in get_skill_by_id
|
||||
response = self.client.get(url, headers=self._get_headers())
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_client.py", line 1053, in get
|
||||
return self.request(
|
||||
^^^^^^^^^^^^^
|
||||
@@ -137,7 +53,6 @@ Traceback (most recent call last):
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_transports\default.py", line 249, in handle_request
|
||||
with map_httpcore_exceptions():
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\anaconda3\Lib\contextlib.py", line 158, in __exit__
|
||||
self.gen.throw(value)
|
||||
File "D:\anaconda3\Lib\site-packages\httpx\_transports\default.py", line 118, in map_httpcore_exceptions
|
||||
@@ -147,12 +62,12 @@ httpx.ConnectError: [Errno 11001] getaddrinfo failed
|
||||
During handling of the above exception, another exception occurred:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 281, in call_third_party_api
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 202, in call_third_party_api
|
||||
raw_result = get_skill_by_id(skill_id)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 208, in get_skill_by_id
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 154, in get_skill_by_id
|
||||
return default_client.get_skill_by_id(skill_id)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 104, in get_skill_by_id
|
||||
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 70, in get_skill_by_id
|
||||
raise Exception(error_msg)
|
||||
Exception: API请求异常: http://lzwcai-demp-corp-manager:8086/datasource/skill/getBySkillId/, 错误: [Errno 11001] getaddrinfo failed
|
||||
Exception: API 请求异常: http://lzwcai-demp-corp-manager:8086/datasource/skill/getBySkillId/, 错误: [Errno 11001] getaddrinfo failed
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,13 +1,13 @@
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
|
||||
# 支持直接运行和模块导入两种方式
|
||||
try:
|
||||
from .utils import load_json, generate_tool_name, generate_input_schema
|
||||
from .utils import get_skill_by_id, DataSourceAPIClient, process_skill_response, test_sql_with_schema
|
||||
from .utils import get_database_id, get_skill_id, get_env_config
|
||||
from .utils import load_json, generate_input_schema
|
||||
from .utils import get_skill_by_id, process_skill_response, test_sql_with_schema
|
||||
from .utils import get_skill_id, get_env_config
|
||||
from .utils.logger_config import logger_config
|
||||
except ImportError:
|
||||
from utils import load_json, generate_input_schema
|
||||
@@ -19,41 +19,43 @@ from mcp.server.models import InitializationOptions
|
||||
from mcp.server import NotificationOptions, Server
|
||||
import mcp.types as types
|
||||
|
||||
# 初始化 MCP 专用日志器
|
||||
|
||||
mcp_logger = logger_config.setup_mcp_logging()
|
||||
|
||||
# ========== 数据源配置 ==========
|
||||
# 数据源类型常量
|
||||
DATA_SOURCE_API = "api" # 仅使用API数据
|
||||
DATA_SOURCE_LOCAL = "local" # 仅使用本地JSON数据
|
||||
DATA_SOURCE_BOTH = "both" # 合并本地和API数据
|
||||
|
||||
# 默认数据源(可修改)
|
||||
DATA_SOURCE_API = "api"
|
||||
DATA_SOURCE_LOCAL = "local"
|
||||
DATA_SOURCE_BOTH = "both"
|
||||
DEFAULT_DATA_SOURCE = DATA_SOURCE_API
|
||||
# ================================
|
||||
|
||||
|
||||
def _text_response(payload: dict[str, Any]) -> list[types.TextContent]:
|
||||
"""Build a JSON text response."""
|
||||
return [
|
||||
types.TextContent(
|
||||
type="text",
|
||||
text=json.dumps(payload, ensure_ascii=False, indent=2)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def _extract_user_id(arguments: dict[str, Any]) -> Any:
|
||||
"""Allow numeric 0, reject None and blank strings."""
|
||||
user_id = arguments.get("userId")
|
||||
if user_id is None:
|
||||
return None
|
||||
if isinstance(user_id, str) and not user_id.strip():
|
||||
return None
|
||||
return user_id
|
||||
|
||||
|
||||
def get_queries():
|
||||
"""
|
||||
获取业务查询配置
|
||||
|
||||
Returns:
|
||||
list: 包含所有业务查询配置的列表
|
||||
"""
|
||||
"""Read local business query config."""
|
||||
try:
|
||||
# 获取当前文件所在目录
|
||||
current_dir = Path(__file__).parent
|
||||
|
||||
# 构建 businessQueries.json 的路径
|
||||
json_path = current_dir / "businessQueries.json"
|
||||
|
||||
mcp_logger.debug(f"正在读取业务查询配置文件: {json_path}")
|
||||
|
||||
# 使用 load_json 方法读取 JSON 文件
|
||||
queries = load_json(json_path)
|
||||
|
||||
mcp_logger.info(f"成功加载 {len(queries)} 个业务查询配置")
|
||||
|
||||
return queries
|
||||
except Exception as e:
|
||||
mcp_logger.error(f"加载业务查询配置失败: {e}", exc_info=True)
|
||||
@@ -61,110 +63,72 @@ def get_queries():
|
||||
|
||||
|
||||
def generate_tool_schema_from_query(query: dict) -> types.Tool:
|
||||
"""
|
||||
根据查询配置生成 MCP 工具模式
|
||||
|
||||
Args:
|
||||
query: 单个查询配置字典
|
||||
|
||||
Returns:
|
||||
types.Tool: MCP 工具对象
|
||||
"""
|
||||
"""Generate an MCP tool definition from one query config."""
|
||||
try:
|
||||
# 获取参数定义并生成 inputSchema
|
||||
parameters = query.get('parameters', {})
|
||||
parameters = query.get("parameters", {})
|
||||
input_schema = generate_input_schema(parameters)
|
||||
|
||||
# 生成工具名称(格式: tool_拼音_id)
|
||||
# tool_name = generate_tool_name(query['businessName'], query['id'])
|
||||
tool_name = query['businessName']
|
||||
# 构建工具描述,包含业务名称和业务描述
|
||||
tool_name = query["businessName"]
|
||||
description = f"{query['businessName']}: {query['businessDescription']}"
|
||||
|
||||
|
||||
mcp_logger.debug(f"生成工具模式: {tool_name} - {query['businessName']}")
|
||||
|
||||
|
||||
return types.Tool(
|
||||
name=tool_name,
|
||||
description=description,
|
||||
inputSchema=input_schema
|
||||
)
|
||||
except Exception as e:
|
||||
mcp_logger.error(f"生成工具模式失败: {query.get('id', 'unknown')}, 错误: {e}", exc_info=True)
|
||||
mcp_logger.error(
|
||||
f"生成工具模式失败: {query.get('id', 'unknown')}, 错误: {e}",
|
||||
exc_info=True
|
||||
)
|
||||
raise
|
||||
|
||||
|
||||
# 创建 MCP 服务器实例
|
||||
server = Server("lzwcai-mcp-sqlexecutor")
|
||||
|
||||
# 缓存查询配置,避免重复加载
|
||||
_queries_cache = None
|
||||
|
||||
|
||||
async def get_queries_cache(source: str = None):
|
||||
"""
|
||||
获取或初始化查询配置缓存
|
||||
|
||||
Args:
|
||||
source: 数据源类型(默认使用 DEFAULT_DATA_SOURCE)
|
||||
- "api": 仅使用API数据
|
||||
- "local": 仅使用本地JSON数据
|
||||
- "both": 合并本地和API数据
|
||||
|
||||
Returns:
|
||||
查询配置列表
|
||||
"""
|
||||
"""Get or initialize query config cache."""
|
||||
global _queries_cache
|
||||
if _queries_cache is None:
|
||||
source = source or DEFAULT_DATA_SOURCE
|
||||
mcp_logger.info(f"初始化查询配置(数据源: {source})...")
|
||||
|
||||
mcp_logger.info(f"初始化查询配置,数据源: {source}")
|
||||
|
||||
if source == DATA_SOURCE_LOCAL:
|
||||
_queries_cache = get_queries()
|
||||
mcp_logger.info(f"本地配置: {len(_queries_cache)} 条")
|
||||
|
||||
elif source == DATA_SOURCE_API:
|
||||
try:
|
||||
_queries_cache = await call_third_party_api()
|
||||
mcp_logger.info(f"API配置: {len(_queries_cache)} 条")
|
||||
mcp_logger.info(f"API配置数组: {_queries_cache}")
|
||||
mcp_logger.info(f"API 配置: {len(_queries_cache)} 条")
|
||||
mcp_logger.info(f"API 配置数组: {_queries_cache}")
|
||||
except Exception as e:
|
||||
mcp_logger.warning(f"API获取失败,降级使用本地配置: {e}")
|
||||
mcp_logger.warning(f"API 获取失败,降级使用本地配置: {e}")
|
||||
_queries_cache = get_queries()
|
||||
|
||||
else: # DATA_SOURCE_BOTH
|
||||
else:
|
||||
local = get_queries()
|
||||
try:
|
||||
api = await call_third_party_api()
|
||||
except Exception as e:
|
||||
mcp_logger.warning(f"API获取失败: {e}")
|
||||
mcp_logger.warning(f"API 获取失败: {e}")
|
||||
api = []
|
||||
_queries_cache = local + api
|
||||
mcp_logger.info(f"配置总数: {len(_queries_cache)} 条(本地{len(local)}+API{len(api)})")
|
||||
|
||||
mcp_logger.info(f"配置总数: {len(_queries_cache)} 条,本地 {len(local)} + API {len(api)}")
|
||||
|
||||
return _queries_cache
|
||||
|
||||
|
||||
@server.list_tools()
|
||||
async def handle_list_tools() -> list[types.Tool]:
|
||||
"""
|
||||
列出所有动态生成的 MCP 工具
|
||||
|
||||
Returns:
|
||||
list[types.Tool]: 所有可用的工具列表
|
||||
"""
|
||||
"""List all dynamically generated MCP tools."""
|
||||
try:
|
||||
mcp_logger.info("收到列出工具请求")
|
||||
|
||||
queries = await get_queries_cache()
|
||||
tools = []
|
||||
|
||||
for query in queries:
|
||||
tool = generate_tool_schema_from_query(query)
|
||||
tools.append(tool)
|
||||
|
||||
tools = [generate_tool_schema_from_query(query) for query in queries]
|
||||
mcp_logger.info(f"成功生成 {len(tools)} 个 MCP 工具")
|
||||
mcp_logger.debug(f"工具列表: {[tool.name for tool in tools]}")
|
||||
|
||||
return tools
|
||||
except Exception as e:
|
||||
mcp_logger.error(f"列出工具失败: {e}", exc_info=True)
|
||||
@@ -173,151 +137,97 @@ async def handle_list_tools() -> list[types.Tool]:
|
||||
|
||||
@server.call_tool()
|
||||
async def handle_call_tool(
|
||||
name: str,
|
||||
name: str,
|
||||
arguments: dict[str, Any] | None
|
||||
) -> list[types.TextContent]:
|
||||
"""
|
||||
处理工具调用请求
|
||||
|
||||
Args:
|
||||
name: 工具名称
|
||||
arguments: 工具参数
|
||||
|
||||
Returns:
|
||||
list[types.TextContent]: 工具执行结果(返回参数和对应的接口配置)
|
||||
"""
|
||||
"""Handle MCP tool invocation."""
|
||||
try:
|
||||
mcp_logger.info(f"收到工具调用请求: {name}")
|
||||
mcp_logger.debug(f"工具参数: {arguments}")
|
||||
|
||||
# 获取查询配置缓存
|
||||
|
||||
queries = await get_queries_cache()
|
||||
|
||||
# 根据工具名称查找对应的 item(接口配置)
|
||||
tool_item = None
|
||||
for query in queries:
|
||||
# tool_name = generate_tool_name(query['businessName'], query['id'])
|
||||
tool_name = query['businessName']
|
||||
if tool_name == name:
|
||||
tool_item = query
|
||||
break
|
||||
|
||||
# 构建返回结果
|
||||
import json
|
||||
|
||||
if tool_item:
|
||||
request_data = {
|
||||
"datasourceId": tool_item.get("datasourceId"),
|
||||
"businessName": tool_item.get("businessName"),
|
||||
"businessDescription": tool_item.get("businessDescription"),
|
||||
"sqlTemplate": tool_item.get("sqlTemplate"),
|
||||
"parameters": tool_item.get("parameters"),
|
||||
"testParams": arguments or {}
|
||||
}
|
||||
|
||||
# 如果 arguments 中有 targetDatabaseName 且有值,添加到 request_data
|
||||
if arguments and arguments.get("targetDatabaseName"):
|
||||
request_data["targetDatabaseName"] = arguments["targetDatabaseName"]
|
||||
mcp_logger.debug(f"添加目标数据库名称: {arguments['targetDatabaseName']}")
|
||||
|
||||
# 如果 arguments 中有 userId 且有值,添加到 request_data
|
||||
if arguments and arguments.get("userId"):
|
||||
request_data["userId"] = arguments["userId"]
|
||||
mcp_logger.debug(f"添加用户ID: {arguments['userId']}")
|
||||
|
||||
# 调用测试SQL API
|
||||
try:
|
||||
mcp_logger.info("正在调用测试SQL API...")
|
||||
api_response = test_sql_with_schema(request_data)
|
||||
mcp_logger.info("测试SQL API调用成功")
|
||||
|
||||
# 只返回 API 响应结果
|
||||
result_text = json.dumps(api_response, ensure_ascii=False, indent=2)
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"调用测试SQL API失败: {str(e)}"
|
||||
mcp_logger.error(error_msg, exc_info=True)
|
||||
result_text = json.dumps({"error": error_msg}, ensure_ascii=False, indent=2)
|
||||
else:
|
||||
error_msg = f"未找到工具 {name} 对应的配置"
|
||||
result_text = json.dumps({"error": error_msg}, ensure_ascii=False, indent=2)
|
||||
|
||||
mcp_logger.debug(f"工具调用结果: {result_text}")
|
||||
|
||||
return [
|
||||
types.TextContent(
|
||||
type="text",
|
||||
text=result_text
|
||||
)
|
||||
]
|
||||
tool_item = next((query for query in queries if query["businessName"] == name), None)
|
||||
|
||||
if not tool_item:
|
||||
return _text_response({"error": f"未找到工具 {name} 对应的配置"})
|
||||
|
||||
normalized_parameters = generate_input_schema(tool_item.get("parameters"))
|
||||
normalized_arguments = arguments or {}
|
||||
user_id = _extract_user_id(normalized_arguments)
|
||||
|
||||
if user_id is None:
|
||||
error_msg = f"工具 {name} 缺少必填参数 userId"
|
||||
mcp_logger.warning(error_msg)
|
||||
return _text_response({"error": error_msg, "required": ["userId"]})
|
||||
|
||||
request_data = {
|
||||
"datasourceId": tool_item.get("datasourceId"),
|
||||
"businessName": tool_item.get("businessName"),
|
||||
"businessDescription": tool_item.get("businessDescription"),
|
||||
"sqlTemplate": tool_item.get("sqlTemplate"),
|
||||
"parameters": normalized_parameters,
|
||||
"testParams": normalized_arguments,
|
||||
"userId": user_id
|
||||
}
|
||||
|
||||
target_database_name = normalized_arguments.get("targetDatabaseName")
|
||||
if target_database_name:
|
||||
request_data["targetDatabaseName"] = target_database_name
|
||||
mcp_logger.debug(f"添加目标数据库名称: {target_database_name}")
|
||||
|
||||
try:
|
||||
mcp_logger.info("正在调用测试 SQL API...")
|
||||
result_payload = test_sql_with_schema(request_data)
|
||||
mcp_logger.info("测试 SQL API 调用成功")
|
||||
except Exception as e:
|
||||
error_msg = f"调用测试 SQL API 失败: {str(e)}"
|
||||
mcp_logger.error(error_msg, exc_info=True)
|
||||
result_payload = {"error": error_msg}
|
||||
|
||||
mcp_logger.debug(f"工具调用结果: {result_payload}")
|
||||
return _text_response(result_payload)
|
||||
except Exception as e:
|
||||
error_msg = f"工具调用失败: {name}, 错误: {e}"
|
||||
mcp_logger.error(error_msg, exc_info=True)
|
||||
return [
|
||||
types.TextContent(
|
||||
type="text",
|
||||
text=f"错误: {error_msg}"
|
||||
)
|
||||
]
|
||||
return [types.TextContent(type="text", text=f"错误: {error_msg}")]
|
||||
|
||||
|
||||
async def call_third_party_api(skill_id: str = None) -> list:
|
||||
"""
|
||||
调用第三方API获取技能信息并返回处理后的数据
|
||||
|
||||
Args:
|
||||
skill_id: 技能ID(默认从环境变量 SKILL_ID 读取,如果未设置则使用 1981000305474482178)
|
||||
|
||||
Returns:
|
||||
处理后的查询配置列表(businessQueries格式)
|
||||
|
||||
Example:
|
||||
queries = await call_third_party_api()
|
||||
# 返回: [{"id": "...", "businessName": "...", ...}, ...]
|
||||
"""
|
||||
"""Call backend API and map the response into query configs."""
|
||||
try:
|
||||
# 如果没有传入 skill_id,则从环境变量读取
|
||||
if skill_id is None:
|
||||
skill_id = get_skill_id()
|
||||
|
||||
mcp_logger.info(f"调用第三方API,skill_id: {skill_id}")
|
||||
|
||||
# 获取原始数据
|
||||
raw_result = get_skill_by_id(skill_id)
|
||||
|
||||
mcp_logger.info(f"成功{raw_result}")
|
||||
|
||||
# 处理并返回
|
||||
mcp_logger.info(f"调用第三方 API,skill_id: {skill_id}")
|
||||
raw_result = get_skill_by_id(skill_id)
|
||||
mcp_logger.info(f"成功获取原始响应: {raw_result}")
|
||||
|
||||
processed_queries = process_skill_response(raw_result)
|
||||
|
||||
mcp_logger.info(f"成功获取并处理 {len(processed_queries)} 条数据")
|
||||
mcp_logger.info(f"成功处理 {len(processed_queries)} 条数据")
|
||||
return processed_queries
|
||||
|
||||
except Exception as e:
|
||||
mcp_logger.error(f"API调用失败: {e}", exc_info=True)
|
||||
mcp_logger.error(f"API 调用失败: {e}", exc_info=True)
|
||||
raise
|
||||
|
||||
|
||||
async def async_main():
|
||||
"""MCP 服务器异步主函数"""
|
||||
"""Async entry for the MCP server."""
|
||||
try:
|
||||
mcp_logger.info("=" * 60)
|
||||
mcp_logger.info("正在启动 MCP 服务器: lzwcai-mcp-sqlexecutor")
|
||||
mcp_logger.info("正在启动 MCP 服务: lzwcai-mcp-sqlexecutor")
|
||||
mcp_logger.info("版本: 0.1.0")
|
||||
mcp_logger.info("=" * 60)
|
||||
|
||||
# 输出环境配置信息
|
||||
|
||||
env_config = get_env_config()
|
||||
mcp_logger.info(f"环境配置 - Database ID: {env_config['database_id']}")
|
||||
mcp_logger.info(f"环境配置 - Skill ID: {env_config['skill_id']}")
|
||||
mcp_logger.info(f"环境配置 - Backend Base URL: {env_config['backend_base_url']}")
|
||||
mcp_logger.info("=" * 60)
|
||||
|
||||
|
||||
from mcp.server.stdio import stdio_server
|
||||
|
||||
|
||||
async with stdio_server() as (read_stream, write_stream):
|
||||
mcp_logger.info("MCP 服务器已启动,等待客户端连接...")
|
||||
|
||||
mcp_logger.info("MCP 服务已启动,等待客户端连接...")
|
||||
await server.run(
|
||||
read_stream,
|
||||
write_stream,
|
||||
@@ -330,30 +240,25 @@ async def async_main():
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
mcp_logger.info("MCP 服务器已关闭")
|
||||
|
||||
|
||||
mcp_logger.info("MCP 服务已关闭")
|
||||
except Exception as e:
|
||||
mcp_logger.error(f"MCP 服务器运行失败: {e}", exc_info=True)
|
||||
mcp_logger.error(f"MCP 服务运行失败: {e}", exc_info=True)
|
||||
raise
|
||||
|
||||
|
||||
def main():
|
||||
"""入口点函数(用于 console_scripts)"""
|
||||
"""Console entrypoint."""
|
||||
try:
|
||||
# 初始化系统日志
|
||||
# MCP协议使用stdio通信,必须禁用控制台输出以避免干扰JSON-RPC通信
|
||||
logger_config.setup_logging(
|
||||
app_name="lzwcai_mcp_sqlexecutor",
|
||||
log_level=logging.INFO,
|
||||
console_output=False # 禁用控制台输出
|
||||
console_output=False
|
||||
)
|
||||
|
||||
mcp_logger.info("开始运行 MCP SQL Executor 服务器")
|
||||
mcp_logger.info("开始运行 MCP SQL Executor 服务")
|
||||
asyncio.run(async_main())
|
||||
|
||||
except KeyboardInterrupt:
|
||||
mcp_logger.info("收到中断信号,正在关闭服务器...")
|
||||
mcp_logger.info("收到中断信号,正在关闭服务...")
|
||||
except Exception as e:
|
||||
mcp_logger.error(f"程序运行失败: {e}", exc_info=True)
|
||||
raise
|
||||
|
||||
@@ -1,330 +1,209 @@
|
||||
"""
|
||||
第三方API调用客户端
|
||||
用于调用外部数据源接口
|
||||
"""
|
||||
"""Backend API client helpers."""
|
||||
|
||||
import json
|
||||
import logging
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
import httpx
|
||||
import logging
|
||||
import json
|
||||
from typing import Dict, Any, Optional, List
|
||||
|
||||
# 支持直接运行和模块导入两种方式
|
||||
try:
|
||||
from .env_config import get_backend_base_url
|
||||
except ImportError:
|
||||
from env_config import get_backend_base_url
|
||||
|
||||
# 获取日志记录器
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DataSourceAPIClient:
|
||||
"""数据源API客户端"""
|
||||
|
||||
"""HTTP client for backend datasource APIs."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
self,
|
||||
base_url: Optional[str] = None,
|
||||
token: Optional[str] = None
|
||||
):
|
||||
"""
|
||||
初始化API客户端
|
||||
|
||||
Args:
|
||||
base_url: API基础URL(默认从环境变量 BACKEND_BASE_URL 读取,如果未设置则使用 http://192.168.2.236:8088)
|
||||
token: 认证令牌(Bearer Token)
|
||||
"""
|
||||
# 如果没有传入 base_url,则从环境变量读取
|
||||
if base_url is None:
|
||||
base_url = get_backend_base_url()
|
||||
|
||||
self.base_url = base_url.rstrip('/')
|
||||
self.token = token or "eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjJiYTk4ODllLWM2ZGItNDQ5YS1iZmFjLTQ2YzMxODFlODg5NCJ9.dvi8zm0LsWvJ_h9zD5blnHFRxa4z4_WBm1R487ekE7HlHzrN6dnvqhK8askqT5b1EcE8myHwRzLVMoI8UOjOrw"
|
||||
|
||||
self.base_url = base_url.rstrip("/")
|
||||
self.token = token or (
|
||||
"eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjJiYTk4ODllLWM2ZGItNDQ5YS1i"
|
||||
"ZmFjLTQ2YzMxODFlODg5NCJ9.dvi8zm0LsWvJ_h9zD5blnHFRxa4z4_WBm1R487ekE7HlHzrN6dn"
|
||||
"vqhK8askqT5b1EcE8myHwRzLVMoI8UOjOrw"
|
||||
)
|
||||
self.client = httpx.Client(timeout=30.0)
|
||||
|
||||
|
||||
def _get_headers(self) -> Dict[str, str]:
|
||||
"""
|
||||
获取请求头
|
||||
|
||||
Returns:
|
||||
请求头字典
|
||||
"""
|
||||
return {
|
||||
'Authorization': f'Bearer {self.token}',
|
||||
"Authorization": f"Bearer {self.token}",
|
||||
}
|
||||
|
||||
|
||||
def get_skill_by_id(self, skill_id: str) -> Dict[str, Any]:
|
||||
"""
|
||||
根据技能ID获取技能信息
|
||||
|
||||
Args:
|
||||
skill_id: 技能ID
|
||||
|
||||
Returns:
|
||||
API响应数据
|
||||
|
||||
Raises:
|
||||
Exception: 请求失败时抛出
|
||||
"""
|
||||
"""Fetch skill details by skill id."""
|
||||
try:
|
||||
url = f"{self.base_url}/datasource/skill/getBySkillId/{skill_id}"
|
||||
|
||||
logger.info(f"正在调用API: {url}")
|
||||
|
||||
logger.info(f"正在调用 API: {url}")
|
||||
logger.debug(f"请求参数 - skill_id: {skill_id}")
|
||||
|
||||
response = self.client.get(
|
||||
url,
|
||||
headers=self._get_headers()
|
||||
)
|
||||
|
||||
# 检查HTTP状态码
|
||||
|
||||
response = self.client.get(url, headers=self._get_headers())
|
||||
response.raise_for_status()
|
||||
|
||||
# 解析JSON响应
|
||||
data = response.json()
|
||||
|
||||
logger.info(f"API调用成功: {url}")
|
||||
|
||||
logger.info(f"API 调用成功: {url}")
|
||||
logger.debug(f"响应数据: {data}")
|
||||
|
||||
return data
|
||||
|
||||
|
||||
except httpx.TimeoutException:
|
||||
error_msg = f"API请求超时: {url}"
|
||||
error_msg = f"API 请求超时: {url}"
|
||||
logger.error(error_msg)
|
||||
raise Exception(error_msg)
|
||||
|
||||
except httpx.HTTPStatusError as e:
|
||||
error_msg = f"API请求失败 (HTTP {e.response.status_code}): {url}"
|
||||
error_msg = f"API 请求失败 (HTTP {e.response.status_code}): {url}"
|
||||
logger.error(error_msg)
|
||||
logger.error(f"错误响应: {e.response.text}")
|
||||
raise Exception(error_msg)
|
||||
|
||||
except httpx.RequestError as e:
|
||||
error_msg = f"API请求异常: {url}, 错误: {str(e)}"
|
||||
error_msg = f"API 请求异常: {url}, 错误: {str(e)}"
|
||||
logger.error(error_msg)
|
||||
raise Exception(error_msg)
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"处理API响应时出错: {str(e)}"
|
||||
error_msg = f"处理 API 响应时出错: {str(e)}"
|
||||
logger.error(error_msg, exc_info=True)
|
||||
raise Exception(error_msg)
|
||||
|
||||
|
||||
def test_sql_with_schema(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
测试SQL语句并返回执行结果
|
||||
|
||||
Args:
|
||||
request_data: 请求数据,包含以下字段:
|
||||
- datasourceId: 数据源ID
|
||||
- businessName: 业务名称
|
||||
- businessDescription: 业务描述
|
||||
- sqlTemplate: SQL模板
|
||||
- parameters: 参数定义
|
||||
- testParams: 测试参数
|
||||
|
||||
Returns:
|
||||
API响应数据
|
||||
|
||||
Raises:
|
||||
Exception: 请求失败时抛出
|
||||
"""
|
||||
"""Call backend SQL test endpoint."""
|
||||
try:
|
||||
# 详细记录传入的数据
|
||||
logger.info("=" * 80)
|
||||
logger.info("test_sql_with_schema 接口接收到的数据:")
|
||||
logger.info(f"数据类型: {type(request_data)}")
|
||||
logger.info(f"数据内容: {json.dumps(request_data, ensure_ascii=False, indent=2)}")
|
||||
logger.info(f"数据源ID: {request_data.get('datasourceId')}")
|
||||
logger.info(f"数据源 ID: {request_data.get('datasourceId')}")
|
||||
logger.info(f"业务名称: {request_data.get('businessName')}")
|
||||
logger.info(f"业务描述: {request_data.get('businessDescription')}")
|
||||
logger.info(f"SQL模板: {request_data.get('sqlTemplate')}")
|
||||
logger.info(f"SQL 模板: {request_data.get('sqlTemplate')}")
|
||||
logger.info(f"参数定义: {request_data.get('parameters')}")
|
||||
logger.info(f"测试参数: {request_data.get('testParams')}")
|
||||
if 'userId' in request_data:
|
||||
logger.info(f"用户ID: {request_data.get('userId')}")
|
||||
if "userId" in request_data:
|
||||
logger.info(f"用户 ID: {request_data.get('userId')}")
|
||||
logger.info("=" * 80)
|
||||
|
||||
|
||||
url = f"{self.base_url}/datasource/sqlExecutionLog/testSqlWithSchema"
|
||||
|
||||
# 构建请求头(包含Content-Type)
|
||||
headers = self._get_headers()
|
||||
headers['Content-Type'] = 'application/json'
|
||||
headers['Accept'] = '*/*'
|
||||
|
||||
logger.info(f"正在调用测试SQL API: {url}")
|
||||
headers["Content-Type"] = "application/json"
|
||||
headers["Accept"] = "*/*"
|
||||
|
||||
logger.info(f"正在调用测试 SQL API: {url}")
|
||||
logger.debug(f"请求数据: {json.dumps(request_data, ensure_ascii=False, indent=2)}")
|
||||
|
||||
# 发送POST请求
|
||||
response = self.client.post(
|
||||
url,
|
||||
headers=headers,
|
||||
json=request_data
|
||||
)
|
||||
|
||||
# 检查HTTP状态码
|
||||
|
||||
response = self.client.post(url, headers=headers, json=request_data)
|
||||
response.raise_for_status()
|
||||
|
||||
# 解析JSON响应
|
||||
data = response.json()
|
||||
|
||||
# 详细记录返回的数据
|
||||
|
||||
logger.info("=" * 80)
|
||||
logger.info("test_sql_with_schema 接口返回的数据:")
|
||||
logger.info(f"HTTP状态码: {response.status_code}")
|
||||
logger.info(f"HTTP 状态码: {response.status_code}")
|
||||
logger.info(f"响应数据类型: {type(data)}")
|
||||
logger.info(f"响应数据内容: {json.dumps(data, ensure_ascii=False, indent=2)}")
|
||||
if isinstance(data, dict):
|
||||
logger.info(f"响应code: {data.get('code')}")
|
||||
logger.info(f"响应msg: {data.get('msg')}")
|
||||
logger.info(f"响应data: {data.get('data')}")
|
||||
logger.info(f"响应 code: {data.get('code')}")
|
||||
logger.info(f"响应 msg: {data.get('msg')}")
|
||||
logger.info(f"响应 data: {data.get('data')}")
|
||||
logger.info("=" * 80)
|
||||
|
||||
logger.info("测试SQL API调用成功")
|
||||
|
||||
|
||||
logger.info("测试 SQL API 调用成功")
|
||||
return data
|
||||
|
||||
|
||||
except httpx.TimeoutException:
|
||||
error_msg = f"测试SQL API请求超时: {url}"
|
||||
error_msg = f"测试 SQL API 请求超时: {url}"
|
||||
logger.error(error_msg)
|
||||
raise Exception(error_msg)
|
||||
|
||||
except httpx.HTTPStatusError as e:
|
||||
error_msg = f"测试SQL API请求失败 (HTTP {e.response.status_code}): {url}"
|
||||
error_msg = f"测试 SQL API 请求失败 (HTTP {e.response.status_code}): {url}"
|
||||
logger.error(error_msg)
|
||||
logger.error(f"错误响应: {e.response.text}")
|
||||
raise Exception(error_msg)
|
||||
|
||||
except httpx.RequestError as e:
|
||||
error_msg = f"测试SQL API请求异常: {url}, 错误: {str(e)}"
|
||||
error_msg = f"测试 SQL API 请求异常: {url}, 错误: {str(e)}"
|
||||
logger.error(error_msg)
|
||||
raise Exception(error_msg)
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"处理测试SQL API响应时出错: {str(e)}"
|
||||
error_msg = f"处理测试 SQL API 响应时出错: {str(e)}"
|
||||
logger.error(error_msg, exc_info=True)
|
||||
raise Exception(error_msg)
|
||||
|
||||
|
||||
def close(self):
|
||||
"""关闭HTTP客户端"""
|
||||
"""Close the underlying HTTP client."""
|
||||
self.client.close()
|
||||
|
||||
|
||||
# 创建默认客户端实例
|
||||
default_client = DataSourceAPIClient()
|
||||
|
||||
|
||||
def get_skill_by_id(skill_id: str, base_url: Optional[str] = None, token: Optional[str] = None) -> Dict[str, Any]:
|
||||
"""
|
||||
便捷函数:根据技能ID获取技能信息
|
||||
|
||||
Args:
|
||||
skill_id: 技能ID
|
||||
base_url: API基础URL(可选,默认从环境变量 BACKEND_BASE_URL 读取)
|
||||
token: 认证令牌(可选,使用默认值)
|
||||
|
||||
Returns:
|
||||
API响应数据
|
||||
"""
|
||||
def get_skill_by_id(
|
||||
skill_id: str,
|
||||
base_url: Optional[str] = None,
|
||||
token: Optional[str] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""Convenience wrapper for skill lookup."""
|
||||
if base_url or token:
|
||||
client = DataSourceAPIClient(
|
||||
base_url=base_url,
|
||||
token=token
|
||||
)
|
||||
client = DataSourceAPIClient(base_url=base_url, token=token)
|
||||
return client.get_skill_by_id(skill_id)
|
||||
else:
|
||||
return default_client.get_skill_by_id(skill_id)
|
||||
return default_client.get_skill_by_id(skill_id)
|
||||
|
||||
|
||||
def test_sql_with_schema(request_data: Dict[str, Any], base_url: Optional[str] = None, token: Optional[str] = None) -> Dict[str, Any]:
|
||||
"""
|
||||
便捷函数:测试SQL语句并返回执行结果
|
||||
|
||||
Args:
|
||||
request_data: 请求数据,包含以下字段:
|
||||
- datasourceId: 数据源ID
|
||||
- businessName: 业务名称
|
||||
- businessDescription: 业务描述
|
||||
- sqlTemplate: SQL模板
|
||||
- parameters: 参数定义
|
||||
- testParams: 测试参数
|
||||
base_url: API基础URL(可选,默认从环境变量 BACKEND_BASE_URL 读取)
|
||||
token: 认证令牌(可选,使用默认值)
|
||||
|
||||
Returns:
|
||||
API响应数据
|
||||
"""
|
||||
def test_sql_with_schema(
|
||||
request_data: Dict[str, Any],
|
||||
base_url: Optional[str] = None,
|
||||
token: Optional[str] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""Convenience wrapper for SQL test endpoint."""
|
||||
if base_url or token:
|
||||
client = DataSourceAPIClient(
|
||||
base_url=base_url,
|
||||
token=token
|
||||
)
|
||||
client = DataSourceAPIClient(base_url=base_url, token=token)
|
||||
return client.test_sql_with_schema(request_data)
|
||||
else:
|
||||
return default_client.test_sql_with_schema(request_data)
|
||||
return default_client.test_sql_with_schema(request_data)
|
||||
|
||||
|
||||
def process_skill_response(response: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
处理API响应数据,映射为businessQueries格式
|
||||
|
||||
Args:
|
||||
response: API原始响应数据
|
||||
|
||||
Returns:
|
||||
处理后的查询配置列表
|
||||
"""
|
||||
"""Map backend skill response into business query configs."""
|
||||
try:
|
||||
# 提取data数组
|
||||
data_list = response.get("data", [])
|
||||
|
||||
# 默认的员工ID参数schema
|
||||
default_employee_schema = {
|
||||
"type": "object",
|
||||
"required": ["employeeId"],
|
||||
"properties": {
|
||||
"employeeId": {
|
||||
"type": "number",
|
||||
"description": "员工ID,用于标识员工的唯一数字标识符",
|
||||
"examples": [1001, 2002]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 映射每个skill为businessQuery格式
|
||||
queries = []
|
||||
|
||||
for skill in data_list:
|
||||
# 解析sqlParams字符串为JSON对象
|
||||
sql_params_str = skill.get("sqlParams") or "{}"
|
||||
sql_params = json.loads(sql_params_str)
|
||||
|
||||
# 判断sqlParams是否为空对象
|
||||
is_empty_params = (
|
||||
not sql_params.get("properties") or
|
||||
len(sql_params.get("properties", {})) == 0
|
||||
) and (
|
||||
not sql_params.get("required") or
|
||||
len(sql_params.get("required", [])) == 0
|
||||
)
|
||||
|
||||
# # 如果是空对象,使用默认的员工ID参数
|
||||
# if is_empty_params:
|
||||
# logger.info(f"技能 {skill.get('name')} (ID: {skill.get('id')}) 的sqlParams为空,使用默认员工ID参数")
|
||||
# sql_params = default_employee_schema
|
||||
|
||||
# 映射字段
|
||||
sql_params_raw = skill.get("sqlParams")
|
||||
sql_params: Dict[str, Any] = {}
|
||||
|
||||
if isinstance(sql_params_raw, dict):
|
||||
sql_params = sql_params_raw
|
||||
elif isinstance(sql_params_raw, str) and sql_params_raw.strip():
|
||||
try:
|
||||
parsed_sql_params = json.loads(sql_params_raw)
|
||||
if isinstance(parsed_sql_params, dict):
|
||||
sql_params = parsed_sql_params
|
||||
else:
|
||||
logger.warning(
|
||||
f"技能 {skill.get('name')} (ID: {skill.get('id')}) 的 sqlParams 不是对象,已回退为空对象"
|
||||
)
|
||||
except json.JSONDecodeError:
|
||||
logger.warning(
|
||||
f"技能 {skill.get('name')} (ID: {skill.get('id')}) 的 sqlParams 不是合法 JSON,已回退为空对象"
|
||||
)
|
||||
|
||||
query = {
|
||||
"id": skill.get("id"),
|
||||
"businessName": skill.get("name"),
|
||||
"businessDescription": skill.get("description"),
|
||||
"sqlTemplate": skill.get("sqlTemplate"),
|
||||
"parameters": sql_params,
|
||||
"datasourceId": skill.get("datasourceId")
|
||||
"datasourceId": skill.get("datasourceId"),
|
||||
}
|
||||
queries.append(query)
|
||||
|
||||
|
||||
logger.info(f"成功处理 {len(queries)} 条技能数据")
|
||||
return queries
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"处理API响应数据失败: {e}", exc_info=True)
|
||||
logger.error(f"处理 API 响应数据失败: {e}", exc_info=True)
|
||||
raise
|
||||
|
||||
|
||||
@@ -1,149 +1,93 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Schema 生成工具模块
|
||||
Schema 生成工具模块。
|
||||
"""
|
||||
|
||||
from copy import deepcopy
|
||||
from typing import Any, Dict
|
||||
|
||||
|
||||
def generate_input_schema(parameters: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
从查询配置的参数定义生成 MCP 工具的 inputSchema
|
||||
|
||||
此函数会保留完整的 JSON Schema 信息,包括:
|
||||
- type: Schema 类型(通常是 "object")
|
||||
- required: 必填字段列表
|
||||
- properties: 属性定义(包括每个属性的 type, description, format, examples 等)
|
||||
- description: Schema 的整体描述(如果有)
|
||||
- 以及其他任何 JSON Schema 标准字段
|
||||
|
||||
此函数还会自动添加以下字段(如果原始 parameters 中未定义):
|
||||
- targetDatabaseName: 目标数据库名称(非必填,默认为空字符串)
|
||||
|
||||
Args:
|
||||
parameters: 查询配置中的参数定义字典,应该是一个完整的 JSON Schema 对象
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: 符合 JSON Schema 规范的 inputSchema 对象
|
||||
|
||||
Example:
|
||||
>>> params = {
|
||||
... "type": "object",
|
||||
... "required": ["userId", "startTime"],
|
||||
... "properties": {
|
||||
... "userId": {
|
||||
... "type": "integer",
|
||||
... "description": "用户的唯一标识符",
|
||||
... "examples": [10086]
|
||||
... },
|
||||
... "startTime": {
|
||||
... "type": "string",
|
||||
... "format": "date-time",
|
||||
... "description": "查询的起始时间",
|
||||
... "examples": ["2023-01-01 00:00:00"]
|
||||
... }
|
||||
... }
|
||||
... }
|
||||
>>> schema = generate_input_schema(params)
|
||||
>>> # schema 将包含所有原始信息,包括 format 和 examples
|
||||
>>> # 同时会自动添加 targetDatabaseName 字段
|
||||
"""
|
||||
# 如果 parameters 本身就是一个完整的 JSON Schema 对象,直接使用
|
||||
# 但确保至少包含 type 和 properties
|
||||
if not parameters:
|
||||
# 如果 parameters 为空,返回一个空的 object schema
|
||||
def _normalize_schema(parameters: Dict[str, Any] | None) -> Dict[str, Any]:
|
||||
"""将任意参数定义归一化为 object schema。"""
|
||||
if not isinstance(parameters, dict) or not parameters:
|
||||
return {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"required": []
|
||||
}
|
||||
|
||||
# 深拷贝 parameters 以避免修改原始数据
|
||||
input_schema = dict(parameters)
|
||||
|
||||
# 确保必需的字段存在
|
||||
if "type" not in input_schema:
|
||||
input_schema["type"] = "object"
|
||||
|
||||
if "properties" not in input_schema:
|
||||
|
||||
input_schema = deepcopy(parameters)
|
||||
input_schema["type"] = "object"
|
||||
|
||||
if not isinstance(input_schema.get("properties"), dict):
|
||||
input_schema["properties"] = {}
|
||||
|
||||
if "required" not in input_schema:
|
||||
|
||||
if not isinstance(input_schema.get("required"), list):
|
||||
input_schema["required"] = []
|
||||
|
||||
# 添加 targetDatabaseName 字段(如果不存在)
|
||||
|
||||
return input_schema
|
||||
|
||||
|
||||
def generate_input_schema(parameters: Dict[str, Any] | None) -> Dict[str, Any]:
|
||||
"""
|
||||
从查询配置的参数定义生成 MCP 工具的 inputSchema。
|
||||
|
||||
会统一补齐:
|
||||
- `targetDatabaseName`:可选
|
||||
- `userId`:必填
|
||||
"""
|
||||
input_schema = _normalize_schema(parameters)
|
||||
|
||||
if "targetDatabaseName" not in input_schema["properties"]:
|
||||
input_schema["properties"]["targetDatabaseName"] = {
|
||||
"type": "string",
|
||||
"description": "目标数据库名称",
|
||||
"default": ""
|
||||
}
|
||||
|
||||
# 添加 userId 字段(如果不存在)
|
||||
|
||||
if "userId" not in input_schema["properties"]:
|
||||
input_schema["properties"]["userId"] = {
|
||||
"type": "string",
|
||||
"description": "当前ai平台用户id;"
|
||||
"description": "当前 AI 平台用户 ID"
|
||||
}
|
||||
|
||||
# 保留所有其他字段,如 description, examples, format 等
|
||||
# JSON Schema 标准支持的字段都会被保留:
|
||||
# - additionalProperties
|
||||
# - patternProperties
|
||||
# - minProperties / maxProperties
|
||||
# - dependencies
|
||||
# - 等等
|
||||
|
||||
|
||||
if "userId" not in input_schema["required"]:
|
||||
input_schema["required"].append("userId")
|
||||
|
||||
return input_schema
|
||||
|
||||
|
||||
def validate_input_schema(schema: Dict[str, Any]) -> tuple[bool, str]:
|
||||
"""
|
||||
验证 inputSchema 是否符合基本的 JSON Schema 规范
|
||||
|
||||
Args:
|
||||
schema: 要验证的 schema 对象
|
||||
|
||||
Returns:
|
||||
tuple[bool, str]: (是否有效, 错误消息或成功消息)
|
||||
|
||||
Example:
|
||||
>>> schema = {"type": "object", "properties": {"id": {"type": "string"}}}
|
||||
>>> is_valid, msg = validate_input_schema(schema)
|
||||
>>> print(is_valid, msg)
|
||||
True, "Schema 验证通过"
|
||||
验证 inputSchema 是否符合基本的 JSON Schema 规范。
|
||||
"""
|
||||
if not isinstance(schema, dict):
|
||||
return False, "Schema 必须是一个字典对象"
|
||||
|
||||
|
||||
if schema.get("type") != "object":
|
||||
return False, "Schema 的 type 字段必须是 'object'"
|
||||
|
||||
|
||||
if "properties" not in schema:
|
||||
return False, "Schema 必须包含 properties 字段"
|
||||
|
||||
|
||||
if not isinstance(schema.get("properties"), dict):
|
||||
return False, "Schema 的 properties 字段必须是一个字典对象"
|
||||
|
||||
# 验证 required 字段(如果存在)
|
||||
|
||||
if "required" in schema:
|
||||
required = schema["required"]
|
||||
if not isinstance(required, list):
|
||||
return False, "Schema 的 required 字段必须是一个列表"
|
||||
|
||||
# 验证所有 required 的字段都在 properties 中定义
|
||||
|
||||
properties = schema["properties"]
|
||||
for field in required:
|
||||
if field not in properties:
|
||||
return False, f"必填字段 '{field}' 未在 properties 中定义"
|
||||
|
||||
# 验证 properties 中每个字段的定义
|
||||
|
||||
for prop_name, prop_def in schema["properties"].items():
|
||||
if not isinstance(prop_def, dict):
|
||||
return False, f"属性 '{prop_name}' 的定义必须是一个字典对象"
|
||||
|
||||
|
||||
if "type" not in prop_def:
|
||||
return False, f"属性 '{prop_name}' 必须包含 type 字段"
|
||||
|
||||
return True, "Schema 验证通过"
|
||||
|
||||
return True, "Schema 验证通过"
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
"""
|
||||
Entry point for lzwcai-mcp-sqlexecutor
|
||||
Runs the MCP server for SQL query execution
|
||||
Repository-local launcher for lzwcai-mcp-sqlexecutor.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
os.environ["databaseId"] = "162"
|
||||
os.environ["skillId"] = "2008360664955854850"
|
||||
os.environ["backendBaseUrl"] = "http://192.168.2.236:8088"
|
||||
if __name__ == "__main__":
|
||||
# Import and run the actual MCP server
|
||||
from lzwcai_mcp_sqlexecutor.main import main
|
||||
|
||||
def main():
|
||||
# Keep local developer defaults without overriding explicit environment settings.
|
||||
os.environ.setdefault("databaseId", "240")
|
||||
os.environ.setdefault("skillId", "2058819964077572098")
|
||||
os.environ.setdefault("backendBaseUrl", "http://192.168.2.236:8088")
|
||||
|
||||
from lzwcai_mcp_sqlexecutor.main import main as package_main
|
||||
|
||||
package_main()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "lzwcai-mcp-sqlexecutor"
|
||||
version = "0.1.12"
|
||||
version = "0.1.13"
|
||||
description = "MCP server for executing business SQL queries with dynamic tool generation"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
|
||||
Reference in New Issue
Block a user