feat(create_mcp): 添加MCP工具输出Schema定义

添加了固定的输出Schema定义,包含code、message和data字段,
用于规范MCP工具的返回格式,提高API响应的一致性。

- 定义了标准的输出Schema结构
- 包含响应状态码、消息和数据字段
- code和message为必需字段
This commit is contained in:
2026-01-04 10:14:23 +08:00
parent ac403b5e6f
commit 384a1fbcb2
28 changed files with 3408 additions and 51 deletions

View File

@@ -0,0 +1,37 @@
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.util.logger_config - INFO - [logger_config.py:215] - ================================================================================
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.util.logger_config - INFO - [logger_config.py:216] - 日志系统初始化完成 - 2025-12-30 11:48:23
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.util.logger_config - INFO - [logger_config.py:217] - 日志级别: INFO
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.util.logger_config - INFO - [logger_config.py:218] - 日志文件: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_api_converter\lzwcai_mcp_api_converter.log
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.util.logger_config - INFO - [logger_config.py:219] - 控制台输出: False
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.util.logger_config - INFO - [logger_config.py:220] - 文件输出: True
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.util.logger_config - INFO - [logger_config.py:221] - 文件轮转: 最大10MB, 保留5个备份
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.util.logger_config - INFO - [logger_config.py:222] - ================================================================================
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.create_mcp - INFO - [create_mcp.py:277] - 开始初始化 MCP 服务器
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.create_mcp - INFO - [create_mcp.py:116] - 配置模式: memory
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.create_mcp - INFO - [create_mcp.py:124] - 使用内存模式加载配置(多租户支持)
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.create_mcp - INFO - [create_mcp.py:135] - 使用环境变量提供的businessUuid: u9ua9ss2l8c
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.create_mcp - INFO - [create_mcp.py:139] - 租户配置变量名: businessu9ua9ss2l8c
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.create_mcp - INFO - [create_mcp.py:147] - 内存中没有租户 u9ua9ss2l8c 的配置,开始从业务平台获取...
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.create_mcp - INFO - [create_mcp.py:168] - 从环境变量bizSysApiIds获取到API IDs: [1970386761072058369, 1970386761185304578, 1970386761583763457, 1970386761420185602]
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.create_mcp - INFO - [create_mcp.py:175] - 调用get_business_api_config获取配置API IDs: [1970386761072058369, 1970386761185304578, 1970386761583763457, 1970386761420185602]
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.business.get_business_api - INFO - [get_business_api.py:193] - 开始获取 4 个API的详情...
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.business.get_business_api - INFO - [get_business_api.py:93] - 成功获取 4 个API详情
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.business.get_business_api - INFO - [get_business_api.py:197] - 开始映射为配置格式...
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.business.get_business_api - INFO - [get_business_api.py:158] - 成功映射 4 个API到配置格式
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.business.get_business_api - INFO - [get_business_api.py:159] - 服务名称: lzwcai_mcp_api_converter
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.business.get_business_api - INFO - [get_business_api.py:160] - 域名URL: https://erp.166bg.com/api
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.business.get_business_api - INFO - [get_business_api.py:161] - 描述: 定时任务列表、定时任务详情、任务列表、任务统计列表(按状态)
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.business.get_business_api - INFO - [get_business_api.py:200] - [SUCCESS] 成功生成API配置包含 4 个API
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.create_mcp - INFO - [create_mcp.py:177] - 成功获取业务API配置包含 4 个API配置
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.create_mcp - INFO - [create_mcp.py:181] - 配置已存储到内存变量: businessu9ua9ss2l8c
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.create_mcp - INFO - [create_mcp.py:182] - 当前内存中共有 1 个租户配置
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.create_mcp - INFO - [create_mcp.py:292] - 服务器配置 - 名称: lzwcai-mcp-dyntoolapi, 版本: 1.0.0
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.core.api_base - INFO - [api_base.py:262] - 开始处理 4 个API配置
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.core.api_base - INFO - [api_base.py:317] - API配置处理完成成功处理 4 个配置
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.core.api_base - INFO - [api_base.py:389] - ApiBase初始化完成共处理 4 个API配置
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.create_mcp - INFO - [create_mcp.py:307] - API 基础服务初始化完成,共 4 个API配置
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.create_mcp - INFO - [create_mcp.py:506] - 注册API工具插件
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.create_mcp - INFO - [create_mcp.py:509] - API工具插件注册完成
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.create_mcp - INFO - [create_mcp.py:779] - 配置文件监控功能已禁用如需启用请设置环境变量ENABLE_CONFIG_WATCH=true
2025-12-30 11:48:23 - lzwcai_mcp_api_converter.src.create_mcp - INFO - [create_mcp.py:578] - 启动STDIO传输模式
2025-12-30 11:48:24 - lzwcai_mcp_api_converter.src.create_mcp - INFO - [create_mcp.py:391] - 返回工具列表,共 4 个工具

View File

@@ -367,12 +367,24 @@ class ApiToolPlugin(ToolPlugin):
tool_name = tool_config["interfaceName"] # 工具名称(拼音格式)
logger.debug(f"注册工具: {tool_name}")
# 定义输出 Schema先写死一个测试
output_schema = {
"type": "object",
"properties": {
"code": {"type": "integer", "description": "响应状态码0表示成功"},
"message": {"type": "string", "description": "响应消息"},
"data": {"type": "object", "description": "响应数据"}
},
"required": ["code", "message"]
}
# 创建MCP工具定义
tools.append(
types.Tool(
name=tool_name, # 工具名称
description=tool_config["schema_description"], # 工具描述(包含参数说明)
inputSchema=tool_config["schema"], # 输入参数的JSON Schema
outputSchema=output_schema, # 输出参数的JSON Schema
)
)

View File

@@ -49,3 +49,226 @@
2025-10-23 15:08:31 - mcp_services - INFO - [main.py:128] - API配置: 1 条
2025-10-23 15:08:31 - mcp_services - INFO - [main.py:129] - API配置数组: [{'id': '1981245768769118209', 'businessName': '数字员工列表查询', 'businessDescription': '查询系统中的所有数字员工信息包括员工ID、名称、类型、状态等核心信息用于展示数字员工管理列表', 'sqlTemplate': 'SELECT id, employee_id, name, employee_type, status, created_at, updated_at, description FROM digital_employees WHERE is_deleted = 0 ORDER BY created_at DESC', 'parameters': {'type': 'object', 'required': ['employeeId'], 'properties': {'employeeId': {'type': 'number', 'description': '员工ID用于标识员工的唯一数字标识符', 'examples': [1001, 2002]}}}, 'datasourceId': '32'}]
2025-10-23 15:08:31 - mcp_services - INFO - [main.py:165] - 成功生成 1 个 MCP 工具
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] - 调用第三方APIskill_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] - 调用第三方APIskill_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 工具

View File

@@ -1,51 +1,223 @@
2025-10-23 15:05:51 - root - INFO - [logger_config.py:151] - 日志系统初始化完成 - 日志目录: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\logs
2025-10-23 15:05:51 - root - INFO - [logger_config.py:152] - 日志配置 - 级别: INFO, 文件大小限制: 10MB, 备份数量: 5
2025-10-23 15:05:51 - mcp_services - INFO - [main.py:341] - 开始运行 MCP SQL Executor 服务器
2025-10-23 15:05:51 - mcp_services - INFO - [main.py:293] - ============================================================
2025-10-23 15:05:51 - mcp_services - INFO - [main.py:294] - 正在启动 MCP 服务器: lzwcai-mcp-sqlexecutor
2025-10-23 15:05:51 - mcp_services - INFO - [main.py:295] - 版本: 0.1.0
2025-10-23 15:05:51 - mcp_services - INFO - [main.py:296] - ============================================================
2025-10-23 15:05:51 - mcp_services - INFO - [main.py:300] - 环境配置 - Database ID: 29
2025-10-23 15:05:51 - mcp_services - INFO - [main.py:301] - 环境配置 - Skill ID: 1981195682443014146
2025-10-23 15:05:51 - mcp_services - INFO - [main.py:302] - 环境配置 - Backend Base URL: http://192.168.2.236:8088
2025-10-23 15:05:51 - mcp_services - INFO - [main.py:303] - ============================================================
2025-10-23 15:05:51 - mcp_services - INFO - [main.py:308] - MCP 服务器已启动,等待客户端连接...
2025-10-23 15:05:52 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type ListToolsRequest
2025-10-23 15:05:52 - mcp_services - INFO - [main.py:156] - 收到列出工具请求
2025-10-23 15:05:52 - mcp_services - INFO - [main.py:119] - 初始化查询配置(数据源: api...
2025-10-23 15:05:52 - mcp_services - INFO - [main.py:272] - 调用第三方APIskill_id: 1981195682443014146
2025-10-23 15:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:71] - 正在调用API: http://192.168.2.236:8088/datasource/skill/getBySkillId/1981195682443014146
2025-10-23 15:05:52 - httpx - INFO - [_client.py:1025] - HTTP Request: GET http://192.168.2.236:8088/datasource/skill/getBySkillId/1981195682443014146 "HTTP/1.1 200 "
2025-10-23 15:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:85] - API调用成功: http://192.168.2.236:8088/datasource/skill/getBySkillId/1981195682443014146
2025-10-23 15:05:52 - mcp_services - INFO - [main.py:277] - 成功{'msg': '查询失败: 技能不存在: 1981195682443014146', 'code': 500}
2025-10-23 15:05:52 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:297] - 成功处理 0 条技能数据
2025-10-23 15:05:52 - mcp_services - INFO - [main.py:282] - 成功获取并处理 0 条数据
2025-10-23 15:05:52 - mcp_services - INFO - [main.py:128] - API配置: 0 条
2025-10-23 15:05:52 - mcp_services - INFO - [main.py:129] - API配置数组: []
2025-10-23 15:05:52 - mcp_services - INFO - [main.py:165] - 成功生成 0 个 MCP 工具
2025-10-23 15:08:30 - root - INFO - [logger_config.py:151] - 日志系统初始化完成 - 日志目录: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\logs
2025-10-23 15:08:30 - root - INFO - [logger_config.py:152] - 日志配置 - 级别: INFO, 文件大小限制: 10MB, 备份数量: 5
2025-10-23 15:08:30 - mcp_services - INFO - [main.py:341] - 开始运行 MCP SQL Executor 服务器
2025-10-23 15:08:30 - mcp_services - INFO - [main.py:293] - ============================================================
2025-10-23 15:08:30 - mcp_services - INFO - [main.py:294] - 正在启动 MCP 服务器: lzwcai-mcp-sqlexecutor
2025-10-23 15:08:30 - mcp_services - INFO - [main.py:295] - 版本: 0.1.0
2025-10-23 15:08:30 - mcp_services - INFO - [main.py:296] - ============================================================
2025-10-23 15:08:30 - mcp_services - INFO - [main.py:300] - 环境配置 - Database ID: 29
2025-10-23 15:08:30 - mcp_services - INFO - [main.py:301] - 环境配置 - Skill ID: 1981245768471322626
2025-10-23 15:08:30 - mcp_services - INFO - [main.py:302] - 环境配置 - Backend Base URL: http://192.168.2.236:8088
2025-10-23 15:08:30 - mcp_services - INFO - [main.py:303] - ============================================================
2025-10-23 15:08:30 - mcp_services - INFO - [main.py:308] - MCP 服务器已启动,等待客户端连接...
2025-10-23 15:08:31 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type ListToolsRequest
2025-10-23 15:08:31 - mcp_services - INFO - [main.py:156] - 收到列出工具请求
2025-10-23 15:08:31 - mcp_services - INFO - [main.py:119] - 初始化查询配置(数据源: api...
2025-10-23 15:08:31 - mcp_services - INFO - [main.py:272] - 调用第三方APIskill_id: 1981245768471322626
2025-10-23 15:08:31 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:71] - 正在调用API: http://192.168.2.236:8088/datasource/skill/getBySkillId/1981245768471322626
2025-10-23 15:08:31 - httpx - INFO - [_client.py:1025] - HTTP Request: GET http://192.168.2.236:8088/datasource/skill/getBySkillId/1981245768471322626 "HTTP/1.1 200 "
2025-10-23 15:08:31 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:85] - API调用成功: http://192.168.2.236:8088/datasource/skill/getBySkillId/1981245768471322626
2025-10-23 15:08:31 - mcp_services - INFO - [main.py:277] - 成功{'msg': '查询成功', 'code': 200, 'data': [{'id': '1981245768769118209', 'createBy': 'yy8z6', 'createTime': '2025-10-23 14:26:16', 'updateBy': None, 'updateTime': None, 'serviceId': '1981245768475516930', 'uniqueName': '数字员工技能描述-SQL服务_数字员工列表查询', 'name': '数字员工列表查询', 'description': '查询系统中的所有数字员工信息包括员工ID、名称、类型、状态等核心信息用于展示数字员工管理列表', 'visualizable': 1, 'toolPrompt': '查询数字员工列表', 'toolType': 'sql', 'datasourceId': '32', 'sqlTemplate': 'SELECT id, employee_id, name, employee_type, status, created_at, updated_at, description FROM digital_employees WHERE is_deleted = 0 ORDER BY created_at DESC', 'sqlParams': '{"type":"object","required":[],"properties":{}}', 'resultType': 'list', 'sourceType': 'ai', 'trainingTaskId': None, 'tableMetadataIds': '889,890,891,892,893,894,895,896', 'executionCount': 0, 'visualizationConfigs': None, 'inputJsonSchema': '{}', 'outputJsonSchema': '{"type":"object","properties":{"data":{"type":"array"}}}', 'lastExecutionTime': None}]}
2025-10-23 15:08:31 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:283] - 技能 数字员工列表查询 (ID: 1981245768769118209) 的sqlParams为空使用默认员工ID参数
2025-10-23 15:08:31 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:297] - 成功处理 1 条技能数据
2025-10-23 15:08:31 - mcp_services - INFO - [main.py:282] - 成功获取并处理 1 条数据
2025-10-23 15:08:31 - mcp_services - INFO - [main.py:128] - API配置: 1 条
2025-10-23 15:08:31 - mcp_services - INFO - [main.py:129] - API配置数组: [{'id': '1981245768769118209', 'businessName': '数字员工列表查询', 'businessDescription': '查询系统中的所有数字员工信息包括员工ID、名称、类型、状态等核心信息用于展示数字员工管理列表', 'sqlTemplate': 'SELECT id, employee_id, name, employee_type, status, created_at, updated_at, description FROM digital_employees WHERE is_deleted = 0 ORDER BY created_at DESC', 'parameters': {'type': 'object', 'required': ['employeeId'], 'properties': {'employeeId': {'type': 'number', 'description': '员工ID用于标识员工的唯一数字标识符', 'examples': [1001, 2002]}}}, 'datasourceId': '32'}]
2025-10-23 15:08:31 - mcp_services - INFO - [main.py:165] - 成功生成 1 个 MCP 工具
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] - 调用第三方APIskill_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] - 调用第三方APIskill_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 工具

View File

@@ -0,0 +1,158 @@
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
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

View File

@@ -34,3 +34,208 @@
2025-10-23 15:08:31 - mcp_services - INFO - [main.py:128] - API配置: 1 条
2025-10-23 15:08:31 - mcp_services - INFO - [main.py:129] - API配置数组: [{'id': '1981245768769118209', 'businessName': '数字员工列表查询', 'businessDescription': '查询系统中的所有数字员工信息包括员工ID、名称、类型、状态等核心信息用于展示数字员工管理列表', 'sqlTemplate': 'SELECT id, employee_id, name, employee_type, status, created_at, updated_at, description FROM digital_employees WHERE is_deleted = 0 ORDER BY created_at DESC', 'parameters': {'type': 'object', 'required': ['employeeId'], 'properties': {'employeeId': {'type': 'number', 'description': '员工ID用于标识员工的唯一数字标识符', 'examples': [1001, 2002]}}}, 'datasourceId': '32'}]
2025-10-23 15:08:31 - mcp_services - INFO - [main.py:165] - 成功生成 1 个 MCP 工具
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_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] - 调用第三方APIskill_id:
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_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 - 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_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_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 - 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_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] - 调用第三方APIskill_id:
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_services - INFO - [main.py:156] - 收到列出工具请求
2025-12-31 15:00:56 - mcp_services - INFO - [main.py:165] - 成功生成 0 个 MCP 工具

View File

@@ -0,0 +1,138 @@
# lzwcai-mcp-sqlexecutor
一个基于 MCP (Model Context Protocol) 的 SQL 查询执行服务器,支持从 JSON 配置文件动态生成查询工具。
## 功能特性
- 🚀 动态工具生成:从 `businessQueries.json` 自动生成 MCP 工具
- 🔧 灵活配置:支持自定义业务查询和参数验证
- 📝 完整日志:详细的操作日志记录
- 🌐 中文支持:工具名称自动转换为拼音
## 安装
### 使用 pip 安装
```bash
pip install lzwcai-mcp-sqlexecutor
```
### 从源码安装
```bash
git clone <repository-url>
cd lzwcai_mcp_sqlexecutor
pip install -e .
```
### 使用 uv 安装(推荐)
```bash
uv pip install lzwcai-mcp-sqlexecutor
```
## 使用方法
### 命令行启动
安装后,可以直接通过命令启动:
```bash
lzwcai-mcp-sqlexecutor
```
### 作为 Python 模块运行
```bash
python -m lzwcai_mcp_sqlexecutor.main
```
### 配置到 MCP 客户端
在你的 MCP 客户端配置文件中添加:
```json
{
"mcpServers": {
"lzwcai-sqlexecutor": {
"command": "lzwcai-mcp-sqlexecutor"
}
}
}
```
## 配置说明
### businessQueries.json
`businessQueries.json` 中定义你的业务查询:
```json
[
{
"id": "query-001",
"businessName": "用户订单查询",
"businessDescription": "根据用户ID查询订单信息",
"sqlTemplate": "SELECT * FROM orders WHERE user_id = {{userId}}",
"parameters": {
"type": "object",
"required": ["userId"],
"properties": {
"userId": {
"type": "integer",
"description": "用户的唯一标识符",
"examples": [10086]
}
}
}
}
]
```
## 开发
### 依赖项
- Python >= 3.13
- httpx >= 0.28.1
- mcp[cli] >= 1.10.1
- pypinyin >= 0.53.0
### 本地开发
```bash
# 克隆仓库
git clone <repository-url>
cd lzwcai_mcp_sqlexecutor
# 安装开发依赖
pip install -e .
# 运行服务器
python -m lzwcai_mcp_sqlexecutor.main
```
## 构建与发布
### 使用 build 构建
```bash
pip install build
python -m build
```
### 发布到 PyPI
```bash
pip install twine
twine upload dist/*
```
## 许可证
MIT License
## 作者
lzwcai

View File

@@ -0,0 +1,10 @@
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info
# Virtual environments
.venv

View File

@@ -0,0 +1,154 @@
# lzwcai-mcp-sqlexecutor
一个基于 MCP (Model Context Protocol) 的 SQL 查询执行服务器,支持从 JSON 配置文件动态生成查询工具。
## 功能特性
- 🚀 动态工具生成:从 `businessQueries.json` 自动生成 MCP 工具
- 🔧 灵活配置:支持自定义业务查询和参数验证
- 📝 完整日志详细的操作日志记录仅输出到文件不干扰MCP通信
- 🌐 中文支持:工具名称自动转换为拼音
## 安装
### 使用 pip 安装
```bash
pip install lzwcai-mcp-sqlexecutor
```
### 从源码安装
```bash
git clone <repository-url>
cd lzwcai_mcp_sqlexecutor
pip install -e .
```
### 使用 uv 安装(推荐)
```bash
uv pip install lzwcai-mcp-sqlexecutor
```
## 使用方法
### 命令行启动
安装后,可以直接通过命令启动:
```bash
lzwcai-mcp-sqlexecutor
```
### 作为 Python 模块运行
```bash
python -m lzwcai_mcp_sqlexecutor.main
```
### 配置到 MCP 客户端
在你的 MCP 客户端配置文件中添加:
```json
{
"mcpServers": {
"lzwcai-sqlexecutor": {
"command": "lzwcai-mcp-sqlexecutor"
}
}
}
```
## 配置说明
### businessQueries.json
`businessQueries.json` 中定义你的业务查询:
```json
[
{
"id": "query-001",
"businessName": "用户订单查询",
"businessDescription": "根据用户ID查询订单信息",
"sqlTemplate": "SELECT * FROM orders WHERE user_id = {{userId}}",
"parameters": {
"type": "object",
"required": ["userId"],
"properties": {
"userId": {
"type": "integer",
"description": "用户的唯一标识符",
"examples": [10086]
}
}
}
}
]
```
## 开发
### 依赖项
- Python >= 3.13
- httpx >= 0.28.1
- mcp[cli] >= 1.10.1
- pypinyin >= 0.53.0
### 本地开发
```bash
# 克隆仓库
git clone <repository-url>
cd lzwcai_mcp_sqlexecutor
# 安装开发依赖
pip install -e .
# 运行服务器
python -m lzwcai_mcp_sqlexecutor.main
```
## 构建与发布
### 使用 build 构建
```bash
pip install build
python -m build
```
### 发布到 PyPI
```bash
pip install twine
twine upload dist/*
```
## 常见问题
### MCP Inspector 显示 JSON 解析错误
如果在使用 MCP Inspector 测试时遇到 `SyntaxError: Unexpected non-whitespace character after JSON` 错误,这是因为:
1. **原因**MCP 协议使用 stdio标准输入输出进行 JSON-RPC 通信,任何输出到 stdout 的内容(如 print 语句或控制台日志)都会破坏 JSON 格式。
2. **解决方案**:本服务器已将所有日志输出配置为仅写入文件(位于 `logs/` 目录),不输出到控制台。日志文件包括:
- `lzwcai_mcp_sqlexecutor.log` - 主日志文件
- `lzwcai_mcp_sqlexecutor_error.log` - 错误日志
- `lzwcai_mcp_sqlexecutor_daily.log` - 按日期滚动的日志
- `mcp_services.log` - MCP 服务专用日志
3. **查看日志**:如果需要调试,请查看 `logs/` 目录下的日志文件。
## 许可证
MIT License
## 作者
lzwcai

View File

@@ -0,0 +1,9 @@
"""
lzwcai-mcp-sqlexecutor - MCP server for executing business SQL queries
"""
__version__ = "0.1.2"
__author__ = "lzwcai"
__all__ = []

View File

@@ -0,0 +1,35 @@
[
{
"id": "2006300000000000000",
"businessName": "查询列表",
"businessDescription": "查询所有工单列表返回fact_work_order表的全部数据",
"datasourceId": "19",
"sqlTemplate": "SELECT * FROM fact_work_order",
"parameters": {}
},
{
"id": "2006300000000000004",
"businessName": "工单详情",
"businessDescription": "根据工单编号查询工单详情,包括工单基本信息、产品信息、委外供应商、工艺路线等",
"datasourceId": "19",
"sqlTemplate": "SELECT wo.work_order_id, wo.work_order_number AS 工单编号, wo.status AS 工单状态, wo.planned_qty AS 计划数量, wo.completed_qty AS 完成数量, wo.operation_good_qty AS 工序良品数, wo.operation_bad_qty AS 工序不良品数, wo.qc_good_qty AS 质检良品数, wo.qc_bad_qty AS 质检不良品数, wo.source_doc_number AS 来源单据编号, wo.event_time_utc AS 事件时间, p.product_code AS 产品编码, p.product_name AS 产品名称, p.product_spec AS 产品规格, s.supplier_name AS 委外供应商, r.routing_name AS 工艺路线 FROM fact_work_order wo LEFT JOIN dim_product p ON wo.product_id = p.product_id AND p.is_current = true LEFT JOIN dim_supplier s ON wo.supplier_id = s.supplier_id AND s.is_current = true LEFT JOIN dim_routing r ON wo.routing_id = r.routing_id AND r.is_current = true WHERE wo.work_order_number = '{workOrderNumber}'",
"parameters": {
"workOrderNumber": {
"type": "string",
"description": "工单编号",
"required": true,
"examples": [
"WO1311343859"
]
}
}
},
{
"id": "2006300000000000005",
"businessName": "工单执行进度与异常节点看板",
"businessDescription": "工单执行进度与异常节点看板,展示工单完工进度、工序良率、质检良率、异常标识等关键监控指标",
"datasourceId": "19",
"sqlTemplate": "WITH work_order_progress AS (SELECT wo.work_order_number, wo.status AS work_order_status, wo.planned_qty, wo.completed_qty, wo.operation_good_qty, wo.operation_bad_qty, wo.qc_good_qty, wo.qc_bad_qty, wo.source_doc_number AS sales_order_number, wo.event_time_utc, p.product_code, p.product_name, CASE WHEN wo.planned_qty > 0 THEN ROUND(wo.completed_qty * 100.0 / wo.planned_qty, 2) ELSE 0 END AS completion_pct, CASE WHEN (wo.operation_good_qty + wo.operation_bad_qty) > 0 THEN ROUND(wo.operation_good_qty * 100.0 / (wo.operation_good_qty + wo.operation_bad_qty), 2) ELSE NULL END AS operation_yield_pct, CASE WHEN (wo.qc_good_qty + wo.qc_bad_qty) > 0 THEN ROUND(wo.qc_good_qty * 100.0 / (wo.qc_good_qty + wo.qc_bad_qty), 2) ELSE NULL END AS qc_yield_pct FROM fact_work_order wo LEFT JOIN dim_product p ON wo.product_id = p.product_id AND p.is_current = true), task_summary AS (SELECT work_order_number, COUNT(*) AS total_tasks, SUM(CASE WHEN actual_end_time_utc IS NOT NULL THEN 1 ELSE 0 END) AS completed_tasks, SUM(CASE WHEN actual_start_time_utc IS NOT NULL AND actual_end_time_utc IS NULL THEN 1 ELSE 0 END) AS in_progress_tasks, SUM(CASE WHEN actual_start_time_utc IS NULL THEN 1 ELSE 0 END) AS pending_tasks, SUM(bad_qty) AS total_bad_qty FROM fact_operation_task GROUP BY work_order_number), labor_summary AS (SELECT work_order_number, SUM(report_qty) AS total_report_qty, SUM(good_qty) AS total_good_qty, SUM(bad_qty) AS total_bad_qty, SUM(duration_minutes) AS total_work_minutes FROM fact_labor_report GROUP BY work_order_number), qc_summary AS (SELECT work_order_number, COUNT(*) AS qc_record_count, SUM(pass_qty) AS total_pass_qty, SUM(fail_qty) AS total_fail_qty FROM fact_quality_inspection WHERE work_order_number IS NOT NULL GROUP BY work_order_number) SELECT wp.work_order_number AS 工单编号, wp.product_code AS 产品编码, wp.product_name AS 产品名称, wp.sales_order_number AS 关联销售订单, wp.work_order_status AS 工单状态, wp.planned_qty AS 计划数量, wp.completed_qty AS 完成数量, wp.completion_pct AS 完工进度百分比, COALESCE(ts.total_tasks, 0) AS 工序总数, COALESCE(ts.completed_tasks, 0) AS 已完成工序, COALESCE(ts.in_progress_tasks, 0) AS 进行中工序, COALESCE(ts.pending_tasks, 0) AS 待开始工序, COALESCE(ls.total_report_qty, 0) AS 累计报工数, COALESCE(ls.total_work_minutes, 0) AS 累计工时_分钟, wp.operation_yield_pct AS 工序良率, wp.qc_yield_pct AS 质检良率, COALESCE(qc.total_pass_qty, 0) AS 质检通过数, COALESCE(qc.total_fail_qty, 0) AS 质检不通过数, CASE WHEN wp.completion_pct < 50 AND wp.work_order_status = 'STARTED' THEN '进度滞后' WHEN wp.operation_yield_pct < 90 THEN '工序良率异常' WHEN wp.qc_yield_pct < 95 THEN '质检良率异常' WHEN ts.total_bad_qty > 0 THEN '存在不良品' ELSE '正常' END AS 异常标识, wp.event_time_utc AS 最后更新时间 FROM work_order_progress wp LEFT JOIN task_summary ts ON wp.work_order_number = ts.work_order_number LEFT JOIN labor_summary ls ON wp.work_order_number = ls.work_order_number LEFT JOIN qc_summary qc ON wp.work_order_number = qc.work_order_number ORDER BY CASE wp.work_order_status WHEN 'STARTED' THEN 1 WHEN 'RELEASED' THEN 2 WHEN 'PLANNED' THEN 3 ELSE 4 END, wp.event_time_utc DESC",
"parameters": {}
}
]

View File

@@ -0,0 +1,358 @@
from pathlib import Path
from typing import Any
import asyncio
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.logger_config import logger_config
except ImportError:
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.logger_config import logger_config
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数据
# 默认数据源(可修改)
DEFAULT_DATA_SOURCE = DATA_SOURCE_LOCAL
# ================================
def get_queries():
"""
获取业务查询配置
Returns:
list: 包含所有业务查询配置的列表
"""
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)
raise
def generate_tool_schema_from_query(query: dict) -> types.Tool:
"""
根据查询配置生成 MCP 工具模式
Args:
query: 单个查询配置字典
Returns:
types.Tool: MCP 工具对象
"""
try:
# 获取参数定义并生成 inputSchema
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']
# 构建工具描述,包含业务名称和业务描述
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)
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:
查询配置列表
"""
global _queries_cache
if _queries_cache is None:
source = source or DEFAULT_DATA_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}")
except Exception as e:
mcp_logger.warning(f"API获取失败降级使用本地配置: {e}")
_queries_cache = get_queries()
else: # DATA_SOURCE_BOTH
local = get_queries()
try:
api = await call_third_party_api()
except Exception as e:
mcp_logger.warning(f"API获取失败: {e}")
api = []
_queries_cache = local + 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]: 所有可用的工具列表
"""
try:
mcp_logger.info("收到列出工具请求")
queries = await get_queries_cache()
tools = []
for query in queries:
tool = generate_tool_schema_from_query(query)
tools.append(tool)
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)
raise
@server.call_tool()
async def handle_call_tool(
name: str,
arguments: dict[str, Any] | None
) -> list[types.TextContent]:
"""
处理工具调用请求
Args:
name: 工具名称
arguments: 工具参数
Returns:
list[types.TextContent]: 工具执行结果(返回参数和对应的接口配置)
"""
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']}")
# 调用测试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
)
]
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}"
)
]
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": "...", ...}, ...]
"""
try:
# 如果没有传入 skill_id则从环境变量读取
if skill_id is None:
skill_id = get_skill_id()
mcp_logger.info(f"调用第三方APIskill_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)} 条数据")
return processed_queries
except Exception as e:
mcp_logger.error(f"API调用失败: {e}", exc_info=True)
raise
async def async_main():
"""MCP 服务器异步主函数"""
try:
mcp_logger.info("=" * 60)
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 服务器已启动,等待客户端连接...")
await server.run(
read_stream,
write_stream,
InitializationOptions(
server_name="lzwcai-mcp-sqlexecutor",
server_version="0.1.0",
capabilities=server.get_capabilities(
notification_options=NotificationOptions(),
experimental_capabilities={},
),
),
)
mcp_logger.info("MCP 服务器已关闭")
except Exception as e:
mcp_logger.error(f"MCP 服务器运行失败: {e}", exc_info=True)
raise
def main():
"""入口点函数(用于 console_scripts"""
try:
# 初始化系统日志
# MCP协议使用stdio通信必须禁用控制台输出以避免干扰JSON-RPC通信
logger_config.setup_logging(
app_name="lzwcai_mcp_sqlexecutor",
log_level=logging.INFO,
console_output=False # 禁用控制台输出
)
mcp_logger.info("开始运行 MCP SQL Executor 服务器")
asyncio.run(async_main())
except KeyboardInterrupt:
mcp_logger.info("收到中断信号,正在关闭服务器...")
except Exception as e:
mcp_logger.error(f"程序运行失败: {e}", exc_info=True)
raise
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,35 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "lzwcai-mcp-sqlexecutor"
version = "0.1.6"
description = "MCP server for executing business SQL queries with dynamic tool generation"
readme = "README.md"
requires-python = ">=3.13"
license = {text = "MIT"}
authors = [
{name = "lzwcai", email = "your-email@example.com"},
]
keywords = ["mcp", "sql", "executor", "server"]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.13",
]
dependencies = [
"httpx>=0.28.1",
"mcp[cli]>=1.10.1",
"pypinyin>=0.53.0",
]
[project.scripts]
lzwcai-mcp-sqlexecutor = "lzwcai_mcp_sqlexecutor.main:main"
[tool.hatch.build.targets.wheel]
packages = ["lzwcai_mcp_sqlexecutor"]
[tool.hatch.build.targets.wheel.force-include]
"lzwcai_mcp_sqlexecutor/businessQueries.json" = "lzwcai_mcp_sqlexecutor/businessQueries.json"

View File

@@ -0,0 +1,24 @@
"""Utils package for lzwcai_mcp_sqlexecutor"""
from .json_helper import load_json
from .name_helper import generate_tool_name
from .schema_helper import generate_input_schema, validate_input_schema
from .api_client import DataSourceAPIClient, get_skill_by_id, process_skill_response, test_sql_with_schema
from .env_config import get_database_id, get_skill_id, get_backend_base_url, get_env_config, set_env_variable
__all__ = [
'load_json',
'generate_tool_name',
'generate_input_schema',
'validate_input_schema',
'DataSourceAPIClient',
'get_skill_by_id',
'process_skill_response',
'test_sql_with_schema',
'get_database_id',
'get_skill_id',
'get_backend_base_url',
'get_env_config',
'set_env_variable'
]

View File

@@ -0,0 +1,318 @@
"""
第三方API调用客户端
用于调用外部数据源接口
"""
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客户端"""
def __init__(
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.client = httpx.Client(timeout=30.0)
def _get_headers(self) -> Dict[str, str]:
"""
获取请求头
Returns:
请求头字典
"""
return {
'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: 请求失败时抛出
"""
try:
url = f"{self.base_url}/datasource/skill/getBySkillId/{skill_id}"
logger.info(f"正在调用API: {url}")
logger.info(f"请求参数 - skill_id: {skill_id}")
response = self.client.get(
url,
headers=self._get_headers()
)
# 检查HTTP状态码
response.raise_for_status()
# 解析JSON响应
data = response.json()
logger.info(f"API调用成功: {url}")
logger.debug(f"响应数据: {data}")
return data
except httpx.TimeoutException:
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}"
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)}"
logger.error(error_msg)
raise Exception(error_msg)
except Exception as 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: 请求失败时抛出
"""
try:
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}")
logger.info(f"请求参数: {json.dumps(request_data, ensure_ascii=False, indent=2)}")
# 发送POST请求
response = self.client.post(
url,
headers=headers,
json=request_data
)
# 检查HTTP状态码
response.raise_for_status()
# 解析JSON响应
result = response.json()
logger.info(f"测试SQL API调用成功")
logger.debug(f"响应数据: {json.dumps(result, ensure_ascii=False, indent=2)}")
# 处理返回数据结构: {code, data: {errorMessage, data}, msg}
# 检查外层 code
if result.get("code") != 200:
error_msg = result.get("msg", "接口返回错误")
logger.error(f"接口返回错误: code={result.get('code')}, msg={error_msg}")
raise Exception(error_msg)
# 检查内层 errorMessage
inner_data = result.get("data", {})
if inner_data.get("errorMessage"):
error_msg = inner_data.get("errorMessage")
logger.error(f"接口业务错误: {error_msg}")
raise Exception(error_msg)
# 返回 data.data
return inner_data.get("data")
except httpx.TimeoutException:
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}"
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)}"
logger.error(error_msg)
raise Exception(error_msg)
except Exception as e:
error_msg = f"处理测试SQL API响应时出错: {str(e)}"
logger.error(error_msg, exc_info=True)
raise Exception(error_msg)
def close(self):
"""关闭HTTP客户端"""
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响应数据
"""
if base_url or 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)
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响应数据
"""
if base_url or 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)
def process_skill_response(response: Dict[str, Any]) -> List[Dict[str, Any]]:
"""
处理API响应数据映射为businessQueries格式
Args:
response: API原始响应数据
Returns:
处理后的查询配置列表
"""
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 = json.loads(skill.get("sqlParams", "{}"))
# 判断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
# 映射字段
query = {
"id": skill.get("id"),
"businessName": skill.get("name"),
"businessDescription": skill.get("description"),
"sqlTemplate": skill.get("sqlTemplate"),
"parameters": sql_params,
"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)
raise

View File

@@ -0,0 +1,88 @@
"""环境变量配置模块"""
import os
from typing import Optional
def get_database_id(default: str = "29") -> str:
"""
获取数据库ID环境变量
Args:
default: 默认值(默认为 "29"
Returns:
str: 数据库ID
Environment Variables:
databaseId: 数据库ID
"""
return os.environ.get("databaseId", default)
def get_skill_id(default: str = "") -> str:
"""
获取技能ID环境变量
Args:
default: 默认值(默认为 ""
Returns:
str: 技能ID
Environment Variables:
skillId: 技能ID
"""
return os.environ.get("skillId", default)
def get_backend_base_url(default: str = "http://lzwcai-demp-corp-manager:8086") -> str:
"""
获取后端API基础URL环境变量
Args:
default: 默认值(默认为 "http://lzwcai-demp-corp-manager:8086"
Returns:
str: 后端API基础URL
Environment Variables:
backendBaseUrl: 后端API基础URL
"""
return os.environ.get("backendBaseUrl", default)
def get_env_config() -> dict:
"""
获取所有环境配置
Returns:
dict: 包含所有配置的字典
Example:
config = get_env_config()
print(config['database_id']) # 输出: "29"
print(config['skill_id']) # 输出: ""
print(config['backend_base_url']) # 输出: "http://lzwcai-demp-corp-manager:8086"
"""
return {
"database_id": get_database_id(),
"skill_id": get_skill_id(),
"backend_base_url": get_backend_base_url()
}
def set_env_variable(key: str, value: str) -> None:
"""
设置环境变量(仅在当前进程中有效)
Args:
key: 环境变量名
value: 环境变量值
Example:
set_env_variable("databaseId", "30")
set_env_variable("skillId", "1234567890")
"""
os.environ[key] = value

View File

@@ -0,0 +1,60 @@
"""JSON 文件读取工具"""
import json
from pathlib import Path
from typing import Any, Union
def load_json(json_path: Union[str, Path]) -> Any:
"""
读取 JSON 文件并返回其内容
Args:
json_path: JSON 文件的路径(支持字符串或 Path 对象)
Returns:
JSON 文件中解析后的数据(可以是字典、列表或其他 JSON 类型)
Raises:
FileNotFoundError: 当文件不存在时
json.JSONDecodeError: 当 JSON 格式无效时
Exception: 其他读取错误
Example:
>>> data = load_json('config.json')
>>> print(data)
{'key': 'value'}
>>> data = load_json(Path('data/users.json'))
>>> print(data)
[{'id': 1, 'name': 'Alice'}]
"""
try:
# 转换为 Path 对象
path = Path(json_path)
# 检查文件是否存在
if not path.exists():
raise FileNotFoundError(f"JSON 文件不存在: {json_path}")
# 检查是否为文件
if not path.is_file():
raise ValueError(f"路径不是一个文件: {json_path}")
# 读取并解析 JSON 文件
with open(path, 'r', encoding='utf-8') as f:
data = json.load(f)
return data
except json.JSONDecodeError as e:
raise json.JSONDecodeError(
f"JSON 格式错误: {e.msg}",
e.doc,
e.pos
)
except FileNotFoundError:
raise
except Exception as e:
raise Exception(f"读取 JSON 文件时发生错误: {str(e)}")

View File

@@ -0,0 +1,489 @@
# -*- coding: utf-8 -*-
"""
统一日志配置模块
提供系统级别的日志配置和管理
"""
import os
import sys
import logging
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler
from datetime import datetime
from pathlib import Path
class LoggerConfig:
"""日志配置管理类"""
def __init__(self, logs_dir: str = None):
"""初始化日志配置
Args:
logs_dir: 日志目录路径默认为项目根目录下的logs文件夹
"""
# 确定日志目录
if logs_dir:
self.logs_dir = Path(logs_dir)
else:
# 获取项目根目录logger_config.py 在 utils 目录下,需要上升两层到达项目根目录)
project_root = Path(__file__).parent.parent
self.logs_dir = project_root / "logs"
# 创建日志目录
self.logs_dir.mkdir(exist_ok=True)
# 日志格式
self.log_format = '%(asctime)s - %(name)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s'
self.date_format = '%Y-%m-%d %H:%M:%S'
# 从环境变量获取日志级别默认为INFO
self.log_level = self._get_log_level_from_env()
# 是否已初始化
self._initialized = False
def _get_log_level_from_env(self) -> int:
"""从环境变量获取日志级别
Returns:
int: 日志级别
"""
log_level_str = os.getenv('LOG_LEVEL', 'INFO').upper()
# 日志级别映射
level_mapping = {
'DEBUG': logging.DEBUG,
'INFO': logging.INFO,
'WARNING': logging.WARNING,
'WARN': logging.WARNING, # 兼容性别名
'ERROR': logging.ERROR,
'CRITICAL': logging.CRITICAL,
'FATAL': logging.CRITICAL # 兼容性别名
}
return level_mapping.get(log_level_str, logging.INFO)
def setup_logging(self,
app_name: str = "lzwcai_mcp_sqlexecutor",
log_level: int = logging.INFO,
max_file_size: int = 10 * 1024 * 1024, # 10MB
backup_count: int = 5,
console_output: bool = True) -> logging.Logger:
"""设置系统日志配置
Args:
app_name: 应用名称,用于日志文件命名
log_level: 日志级别
max_file_size: 单个日志文件最大大小(字节)
backup_count: 保留的备份文件数量
console_output: 是否输出到控制台
Returns:
logging.Logger: 配置好的根日志器
"""
if self._initialized:
return logging.getLogger()
# 设置根日志器
root_logger = logging.getLogger()
root_logger.setLevel(log_level)
# 清除现有的处理器
for handler in root_logger.handlers[:]:
root_logger.removeHandler(handler)
# 创建格式化器
formatter = logging.Formatter(self.log_format, self.date_format)
# 1. 主日志文件 - 按大小滚动
main_log_file = self.logs_dir / f"{app_name}.log"
file_handler = RotatingFileHandler(
main_log_file,
maxBytes=max_file_size,
backupCount=backup_count,
encoding='utf-8'
)
file_handler.setLevel(log_level)
file_handler.setFormatter(formatter)
root_logger.addHandler(file_handler)
# 2. 错误日志文件 - 只记录ERROR及以上级别
error_log_file = self.logs_dir / f"{app_name}_error.log"
error_handler = RotatingFileHandler(
error_log_file,
maxBytes=max_file_size,
backupCount=backup_count,
encoding='utf-8'
)
error_handler.setLevel(logging.ERROR)
error_handler.setFormatter(formatter)
root_logger.addHandler(error_handler)
# 3. 按日期滚动的日志文件
daily_log_file = self.logs_dir / f"{app_name}_daily.log"
daily_handler = TimedRotatingFileHandler(
daily_log_file,
when='midnight',
interval=1,
backupCount=30, # 保留30天
encoding='utf-8'
)
daily_handler.setLevel(log_level)
daily_handler.setFormatter(formatter)
daily_handler.suffix = "%Y-%m-%d"
root_logger.addHandler(daily_handler)
# 4. 控制台输出
# 重要MCP协议使用stdio时必须将日志输出到stderrstdout仅用于JSON-RPC通信
if console_output:
console_handler = logging.StreamHandler(sys.stderr)
console_handler.setLevel(log_level)
console_formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s',
self.date_format
)
console_handler.setFormatter(console_formatter)
root_logger.addHandler(console_handler)
self._initialized = True
# 记录初始化信息
root_logger.info(f"日志系统初始化完成 - 日志目录: {self.logs_dir}")
root_logger.info(f"日志配置 - 级别: {logging.getLevelName(log_level)}, 文件大小限制: {max_file_size//1024//1024}MB, 备份数量: {backup_count}")
return root_logger
def get_module_logger(self, module_name: str) -> logging.Logger:
"""获取模块专用日志器
Args:
module_name: 模块名称
Returns:
logging.Logger: 模块日志器
"""
return logging.getLogger(module_name)
def create_component_logger(self,
component_name: str,
log_file: str = None,
log_level: int = None) -> logging.Logger:
"""为特定组件创建独立的日志器
Args:
component_name: 组件名称
log_file: 独立日志文件名(可选)
log_level: 日志级别(可选)
Returns:
logging.Logger: 组件日志器
"""
logger = logging.getLogger(component_name)
if log_file:
# 为组件创建独立的日志文件
component_log_file = self.logs_dir / log_file
handler = RotatingFileHandler(
component_log_file,
maxBytes=5 * 1024 * 1024, # 5MB
backupCount=3,
encoding='utf-8'
)
formatter = logging.Formatter(self.log_format, self.date_format)
handler.setFormatter(formatter)
if log_level:
handler.setLevel(log_level)
logger.addHandler(handler)
logger.info(f"组件日志器创建完成: {component_name} -> {component_log_file}")
return logger
def setup_mqtt_logging(self) -> logging.Logger:
"""设置MQTT专用日志
Returns:
logging.Logger: MQTT日志器
"""
return self.create_component_logger(
"mqtt_communication",
"mqtt_communication.log",
logging.DEBUG
)
def setup_mcp_logging(self) -> logging.Logger:
"""设置MCP专用日志
Returns:
logging.Logger: MCP日志器
"""
return self.create_component_logger(
"mcp_services",
"mcp_services.log",
logging.DEBUG
)
def setup_api_logging(self) -> logging.Logger:
"""设置API专用日志
Returns:
logging.Logger: API日志器
"""
return self.create_component_logger(
"api_requests",
"api_requests.log",
logging.INFO
)
def get_logs_info(self) -> dict:
"""获取日志系统信息
Returns:
dict: 日志系统信息
"""
log_files = []
if self.logs_dir.exists():
for log_file in self.logs_dir.glob("*.log*"):
stat = log_file.stat()
log_files.append({
"name": log_file.name,
"size": stat.st_size,
"modified": datetime.fromtimestamp(stat.st_mtime).isoformat()
})
return {
"logs_directory": str(self.logs_dir),
"initialized": self._initialized,
"log_files": log_files,
"total_files": len(log_files)
}
def cleanup_old_logs(self, days: int = 30):
"""清理旧日志文件
Args:
days: 保留天数
"""
if not self.logs_dir.exists():
return
from datetime import timedelta
cutoff_time = datetime.now() - timedelta(days=days)
cleaned_files = []
for log_file in self.logs_dir.glob("*.log*"):
if log_file.stat().st_mtime < cutoff_time.timestamp():
try:
log_file.unlink()
cleaned_files.append(log_file.name)
except Exception as e:
logging.error(f"清理日志文件失败: {log_file.name}, 错误: {e}")
if cleaned_files:
logging.info(f"清理了 {len(cleaned_files)} 个旧日志文件: {cleaned_files}")
def set_log_level(self, level: int, logger_name: str = None):
"""动态调整日志级别
Args:
level: 新的日志级别 (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL)
logger_name: 指定日志器名称None表示调整根日志器
"""
if logger_name:
logger = logging.getLogger(logger_name)
else:
logger = logging.getLogger()
old_level = logger.level
logger.setLevel(level)
# 同时调整所有处理器的级别
for handler in logger.handlers:
if not isinstance(handler, logging.StreamHandler) or handler.stream not in (sys.stdout, sys.stderr):
# 不调整控制台处理器的级别,保持原有设置
handler.setLevel(level)
level_name = logging.getLevelName(level)
old_level_name = logging.getLevelName(old_level)
target = logger_name or "根日志器"
logger.info(f"日志级别已调整: {target} {old_level_name} -> {level_name}")
def set_temporary_log_level(self, level: int, logger_name: str = None):
"""临时调整日志级别(会保存原始级别用于恢复)
Args:
level: 临时日志级别
logger_name: 指定日志器名称None表示调整根日志器
"""
if not hasattr(self, '_original_levels'):
self._original_levels = {}
target_name = logger_name or 'root'
if logger_name:
logger = logging.getLogger(logger_name)
else:
logger = logging.getLogger()
# 保存原始级别
if target_name not in self._original_levels:
self._original_levels[target_name] = logger.level
# 设置新级别
self.set_log_level(level, logger_name)
level_name = logging.getLevelName(level)
target = logger_name or "根日志器"
logger.info(f"临时调整日志级别: {target} -> {level_name} (可通过restore_log_level恢复)")
def restore_log_level(self, logger_name: str = None):
"""恢复日志级别到调整前的状态
Args:
logger_name: 指定日志器名称None表示恢复根日志器
"""
if not hasattr(self, '_original_levels'):
logging.warning("没有找到保存的原始日志级别")
return
target_name = logger_name or 'root'
if target_name not in self._original_levels:
logging.warning(f"没有找到 {target_name} 的原始日志级别")
return
original_level = self._original_levels[target_name]
self.set_log_level(original_level, logger_name)
# 清除保存的级别
del self._original_levels[target_name]
target = logger_name or "根日志器"
level_name = logging.getLevelName(original_level)
logging.info(f"已恢复日志级别: {target} -> {level_name}")
def get_current_log_levels(self) -> dict:
"""获取当前所有日志器的级别信息
Returns:
dict: 日志器级别信息
"""
levels_info = {}
# 根日志器
root_logger = logging.getLogger()
levels_info['root'] = {
'level': root_logger.level,
'level_name': logging.getLevelName(root_logger.level),
'handlers_count': len(root_logger.handlers)
}
# 其他已创建的日志器
for name, logger in logging.Logger.manager.loggerDict.items():
if isinstance(logger, logging.Logger):
levels_info[name] = {
'level': logger.level,
'level_name': logging.getLevelName(logger.level),
'handlers_count': len(logger.handlers)
}
return levels_info
# 全局日志配置实例
logger_config = LoggerConfig()
def setup_system_logging(app_name: str = "lzwcai_mcp_sqlexecutor",
log_level: int = logging.INFO) -> logging.Logger:
"""系统日志初始化快捷函数
Args:
app_name: 应用名称
log_level: 日志级别
Returns:
logging.Logger: 根日志器
"""
return logger_config.setup_logging(app_name, log_level)
def get_logger(name: str) -> logging.Logger:
"""获取日志器的快捷函数
Args:
name: 日志器名称
Returns:
logging.Logger: 日志器实例
"""
return logger_config.get_module_logger(name)
def set_log_level(level: int, logger_name: str = None):
"""动态调整日志级别的快捷函数
Args:
level: 新的日志级别 (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL)
logger_name: 指定日志器名称None表示调整根日志器
Examples:
# 调整根日志器为DEBUG级别
set_log_level(logging.DEBUG)
# 调整特定模块日志器为WARNING级别
set_log_level(logging.WARNING, "agent_ontology.core")
"""
logger_config.set_log_level(level, logger_name)
def set_temporary_log_level(level: int, logger_name: str = None):
"""临时调整日志级别的快捷函数
Args:
level: 临时日志级别
logger_name: 指定日志器名称None表示调整根日志器
Examples:
# 临时调整为DEBUG级别进行调试
set_temporary_log_level(logging.DEBUG)
# ... 进行调试 ...
# 恢复原始级别
restore_log_level()
"""
logger_config.set_temporary_log_level(level, logger_name)
def restore_log_level(logger_name: str = None):
"""恢复日志级别的快捷函数
Args:
logger_name: 指定日志器名称None表示恢复根日志器
"""
logger_config.restore_log_level(logger_name)
def get_current_log_levels() -> dict:
"""获取当前日志级别信息的快捷函数
Returns:
dict: 日志器级别信息
Examples:
levels = get_current_log_levels()
print(f"根日志器级别: {levels['root']['level_name']}")
"""
return logger_config.get_current_log_levels()
# 便捷的日志级别常量
class LogLevel:
"""日志级别常量类"""
DEBUG = logging.DEBUG
INFO = logging.INFO
WARNING = logging.WARNING
ERROR = logging.ERROR
CRITICAL = logging.CRITICAL

View File

@@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
"""
名称生成工具模块
"""
from pypinyin import lazy_pinyin, Style
import logging
logger = logging.getLogger(__name__)
def generate_tool_name(business_name: str, tool_id: str) -> str:
"""
根据业务名称和ID生成工具名称
格式: tool_拼音_id
Args:
business_name: 业务名称(中文)
tool_id: 工具ID
Returns:
str: 格式化的工具名称
"""
try:
# 将中文转换为拼音(无音调,小写)
pinyin_list = lazy_pinyin(business_name, style=Style.NORMAL)
# 拼接拼音
pinyin_str = ''.join(pinyin_list)
# 将 ID 中的 '-' 替换为 '_'
formatted_id = tool_id.replace('-', '_')
# 组合成最终的工具名称
tool_name = f"tool_{pinyin_str}_{formatted_id}"
return tool_name
except Exception as e:
logger.error(f"生成工具名称失败: {business_name}, {tool_id}, 错误: {e}", exc_info=True)
# 降级处理:如果拼音转换失败,使用 ID
return f"tool_{tool_id.replace('-', '_')}"

View File

@@ -0,0 +1,166 @@
# -*- coding: utf-8 -*-
"""
Schema 生成工具模块
"""
from typing import Any, Dict, List
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
return {
"type": "object",
"properties": {},
"required": []
}
# 检查 parameters 是否已经是完整的 JSON Schema 格式
# 完整格式应该有 "type": "object" 和 "properties" 字段
if "type" in parameters and parameters.get("type") == "object" and "properties" in parameters:
# 已经是完整的 JSON Schema 格式,直接使用
input_schema = dict(parameters)
else:
# parameters 是简化格式(直接是参数定义),需要转换为 JSON Schema 格式
# 例如: {"workOrderNumber": {"type": "string", "description": "...", "required": true}}
properties = {}
required = []
for param_name, param_def in parameters.items():
if isinstance(param_def, dict):
# 提取 required 标记
if param_def.get("required", False):
required.append(param_name)
# 复制参数定义,但移除 required 字段(它应该在顶层的 required 数组中)
prop_def = {k: v for k, v in param_def.items() if k != "required"}
properties[param_name] = prop_def
input_schema = {
"type": "object",
"properties": properties,
"required": required
}
# 确保必需的字段存在
if "type" not in input_schema:
input_schema["type"] = "object"
if "properties" not in input_schema:
input_schema["properties"] = {}
if "required" not in input_schema:
input_schema["required"] = []
# 添加 targetDatabaseName 字段(如果不存在)
if "targetDatabaseName" not in input_schema["properties"]:
input_schema["properties"]["targetDatabaseName"] = {
"type": "string",
"description": "目标数据库名称",
"default": ""
}
# 保留所有其他字段,如 description, examples, format 等
# JSON Schema 标准支持的字段都会被保留:
# - additionalProperties
# - patternProperties
# - minProperties / maxProperties
# - dependencies
# - 等等
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 验证通过"
"""
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 验证通过"

View File

@@ -0,0 +1,497 @@
version = 1
revision = 2
requires-python = ">=3.13"
[[package]]
name = "annotated-types"
version = "0.7.0"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/aff/07c09a53a08bc/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/1f0/2e8b43a8fbbc3/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53" },
]
[[package]]
name = "anyio"
version = "4.11.0"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
dependencies = [
{ name = "idna" },
{ name = "sniffio" },
]
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/82a/8d0b81e318cc5/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/028/7e96f4d26d414/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc" },
]
[[package]]
name = "attrs"
version = "25.4.0"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/16d/5969b87f0859e/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/adc/f7e2a1fb3b36a/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373" },
]
[[package]]
name = "certifi"
version = "2025.10.5"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/47c/09d31ccf2acf0/certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/0f2/12c2744a9bb6d/certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de" },
]
[[package]]
name = "click"
version = "8.3.0"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/e7b/8232224eba16f/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/9b9/f285302c6e306/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc" },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/086/95f5cb7ed6e05/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/4f1/d9991f5acc0ca/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" },
]
[[package]]
name = "h11"
version = "0.16.0"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/4e3/5b956cf45792e/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/63c/f8bbe7522de3b/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86" },
]
[[package]]
name = "httpcore"
version = "1.0.9"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
dependencies = [
{ name = "certifi" },
{ name = "h11" },
]
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/6e3/4463af53fd2ab/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/2d4/00746a40668fc/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55" },
]
[[package]]
name = "httpx"
version = "0.28.1"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
dependencies = [
{ name = "anyio" },
{ name = "certifi" },
{ name = "httpcore" },
{ name = "idna" },
]
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/75e/98c5f16b0f35b/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/d90/9fcccc110f8c7/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad" },
]
[[package]]
name = "httpx-sse"
version = "0.4.3"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/9b1/ed0127459a660/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/0ac/1c9fe3c0afad2/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc" },
]
[[package]]
name = "idna"
version = "3.11"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/795/dafcc9c04ed0c/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/771/a87f49d9defaf/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea" },
]
[[package]]
name = "jsonschema"
version = "4.25.1"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
dependencies = [
{ name = "attrs" },
{ name = "jsonschema-specifications" },
{ name = "referencing" },
{ name = "rpds-py" },
]
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/e4a/9655ce0da0c0b/jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/3fb/a0169e345c717/jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63" },
]
[[package]]
name = "jsonschema-specifications"
version = "2025.9.1"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
dependencies = [
{ name = "referencing" },
]
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/b54/0987f239e7456/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/988/02fee3a11ee76/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe" },
]
[[package]]
name = "lzwcai-mcp-sqlexecutor"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "httpx" },
{ name = "mcp", extra = ["cli"] },
]
[package.metadata]
requires-dist = [
{ name = "httpx", specifier = ">=0.28.1" },
{ name = "mcp", extras = ["cli"], specifier = ">=1.10.1" },
]
[[package]]
name = "markdown-it-py"
version = "4.0.0"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
dependencies = [
{ name = "mdurl" },
]
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/cb0/a2b4aa34f932c/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/873/27c59b172c501/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147" },
]
[[package]]
name = "mcp"
version = "1.10.1"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
dependencies = [
{ name = "anyio" },
{ name = "httpx" },
{ name = "httpx-sse" },
{ name = "jsonschema" },
{ name = "pydantic" },
{ name = "pydantic-settings" },
{ name = "python-multipart" },
{ name = "sse-starlette" },
{ name = "starlette" },
{ name = "uvicorn", marker = "sys_platform != 'emscripten'" },
]
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/aaa/0957d8307feef/mcp-1.10.1.tar.gz", hash = "sha256:aaa0957d8307feeff180da2d9d359f2b801f35c0c67f1882136239055ef034c2" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/4d0/8301aefe906dc/mcp-1.10.1-py3-none-any.whl", hash = "sha256:4d08301aefe906dce0fa482289db55ce1db831e3e67212e65b5e23ad8454b3c5" },
]
[package.optional-dependencies]
cli = [
{ name = "python-dotenv" },
{ name = "typer" },
]
[[package]]
name = "mdurl"
version = "0.1.2"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/bb4/13d29f5eea38f/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/840/08a41e51615a4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8" },
]
[[package]]
name = "pydantic"
version = "2.12.2"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
dependencies = [
{ name = "annotated-types" },
{ name = "pydantic-core" },
{ name = "typing-extensions" },
{ name = "typing-inspection" },
]
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/7b8/fa15b831a4bbd/pydantic-2.12.2.tar.gz", hash = "sha256:7b8fa15b831a4bbde9d5b84028641ac3080a4ca2cbd4a621a661687e741624fd" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/25f/f718ee909acd8/pydantic-2.12.2-py3-none-any.whl", hash = "sha256:25ff718ee909acd82f1ff9b1a4acfd781bb23ab3739adaa7144f19a6a4e231ae" },
]
[[package]]
name = "pydantic-core"
version = "2.41.4"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/70e/47929a9d4a190/pydantic_core-2.41.4.tar.gz", hash = "sha256:70e47929a9d4a1905a67e4b687d5946026390568a8e952b92824118063cee4d5" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/85e/050ad9e5f6fe1/pydantic_core-2.41.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:85e050ad9e5f6fe1004eec65c914332e52f429bc0ae12d6fa2092407a462c746" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/e73/93f1d64792763/pydantic_core-2.41.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7393f1d64792763a48924ba31d1e44c2cfbc05e3b1c2c9abb4ceeadd912cced" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/94d/ab0940b0d1fb2/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94dab0940b0d1fb28bcab847adf887c66a27a40291eedf0b473be58761c9799a" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/de7/c42f897e689ee/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de7c42f897e689ee6f9e93c4bec72b99ae3b32a2ade1c7e4798e690ff5246e02" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/664/b319919326227/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:664b3199193262277b8b3cd1e754fb07f2c6023289c815a1e1e8fb415cb247b1" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/d95/b253b88f7d308/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d95b253b88f7d308b1c0b417c4624f44553ba4762816f94e6986819b9c273fb2" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/a13/51f5bbdbbabc6/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1351f5bbdbbabc689727cb91649a00cb9ee7203e0a6e54e9f5ba9e22e384b84" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/1af/fa4798520b148/pydantic_core-2.41.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1affa4798520b148d7182da0615d648e752de4ab1a9566b7471bc803d88a062d" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/7b7/4e18052fea4aa/pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7b74e18052fea4aa8dea2fb7dbc23d15439695da6cbe6cfc1b694af1115df09d" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/285/b643d75c0e30a/pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:285b643d75c0e30abda9dc1077395624f314a37e3c09ca402d4015ef5979f1a2" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/f52/679ff4218d713/pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f52679ff4218d713b3b33f88c89ccbf3a5c2c12ba665fb80ccc4192b4608dbab" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/ecd/e6dedd6fff127/pydantic_core-2.41.4-cp313-cp313-win32.whl", hash = "sha256:ecde6dedd6fff127c273c76821bb754d793be1024bc33314a120f83a3c69460c" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/d08/1a1f3800f0540/pydantic_core-2.41.4-cp313-cp313-win_amd64.whl", hash = "sha256:d081a1f3800f05409ed868ebb2d74ac39dd0c1ff6c035b5162356d76030736d4" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/f8e/49c9c364a7edc/pydantic_core-2.41.4-cp313-cp313-win_arm64.whl", hash = "sha256:f8e49c9c364a7edcbe2a310f12733aad95b022495ef2a8d653f645e5d20c1564" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/ed9/7fd56a561f5eb/pydantic_core-2.41.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ed97fd56a561f5eb5706cebe94f1ad7c13b84d98312a05546f2ad036bafe87f4" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/a87/0c307bf1ee91f/pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a870c307bf1ee91fc58a9a61338ff780d01bfae45922624816878dce784095d2" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/d25/e97bc1f5f8f79/pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25e97bc1f5f8f7985bdc2335ef9e73843bb561eb1fa6831fdfc295c1c2061cf" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/d40/5d14bea042f16/pydantic_core-2.41.4-cp313-cp313t-win_amd64.whl", hash = "sha256:d405d14bea042f166512add3091c1af40437c2e7f86988f3915fabd27b1e9cd2" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/19f/3684868309db5/pydantic_core-2.41.4-cp313-cp313t-win_arm64.whl", hash = "sha256:19f3684868309db5263a11bace3c45d93f6f24afa2ffe75a647583df22a2ff89" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/e92/05d97ed08a82e/pydantic_core-2.41.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:e9205d97ed08a82ebb9a307e92914bb30e18cdf6f6b12ca4bedadb1588a0bfe1" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/82d/f1f432b37d832/pydantic_core-2.41.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:82df1f432b37d832709fbcc0e24394bba04a01b6ecf1ee87578145c19cde12ac" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/fc3/b4cc4539e055c/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3b4cc4539e055cfa39a3763c939f9d409eb40e85813257dcd761985a108554" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/b1e/b1754fce47c63/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b1eb1754fce47c63d2ff57fdb88c351a6c0150995890088b33767a10218eaa4e" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/e6a/b5ab30ef325b4/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6ab5ab30ef325b443f379ddb575a34969c333004fca5a1daa0133a6ffaad616" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/31a/41030b1d9ca49/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31a41030b1d9ca497634092b46481b937ff9397a86f9f51bd41c4767b6fc04af" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/a44/ac1738591472c/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a44ac1738591472c3d020f61c6df1e4015180d6262ebd39bf2aeb52571b60f12" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/d72/f2b5e6e82ab8f/pydantic_core-2.41.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d72f2b5e6e82ab8f94ea7d0d42f83c487dc159c5240d8f83beae684472864e2d" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/c4d/1e854aaf04448/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c4d1e854aaf044487d31143f541f7aafe7b482ae72a022c664b2de2e466ed0ad" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/b56/8af94267729d7/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b568af94267729d76e6ee5ececda4e283d07bbb28e8148bb17adad93d025d25a" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/6d5/5fb8b1e8929b3/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6d55fb8b1e8929b341cc313a81a26e0d48aa3b519c1dbaadec3a6a2b4fcad025" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/5b6/6584e549e2e32/pydantic_core-2.41.4-cp314-cp314-win32.whl", hash = "sha256:5b66584e549e2e32a1398df11da2e0a7eff45d5c2d9db9d5667c5e6ac764d77e" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/557/a0aab88664cc5/pydantic_core-2.41.4-cp314-cp314-win_amd64.whl", hash = "sha256:557a0aab88664cc552285316809cab897716a372afaf8efdbef756f8b890e894" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/3f1/ea6f48a045745/pydantic_core-2.41.4-cp314-cp314-win_arm64.whl", hash = "sha256:3f1ea6f48a045745d0d9f325989d8abd3f1eaf47dd00485912d1a3a63c623a8d" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/6c1/fe4c5404c448b/pydantic_core-2.41.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6c1fe4c5404c448b13188dd8bd2ebc2bdd7e6727fa61ff481bcc2cca894018da" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/523/e7da4d43b113b/pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:523e7da4d43b113bf8e7b49fa4ec0c35bf4fe66b2230bfc5c13cc498f12c6c3e" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/572/9225de81fb65b/pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5729225de81fb65b70fdb1907fcf08c75d498f4a6f15af005aabb1fdadc19dfa" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/de2/cfbb09e88f0f7/pydantic_core-2.41.4-cp314-cp314t-win_amd64.whl", hash = "sha256:de2cfbb09e88f0f795fd90cf955858fc2c691df65b1f21f0aa00b99f3fbc661d" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/d34/f950ae05a83e0/pydantic_core-2.41.4-cp314-cp314t-win_arm64.whl", hash = "sha256:d34f950ae05a83e0ede899c595f312ca976023ea1db100cd5aa188f7005e3ab0" },
]
[[package]]
name = "pydantic-settings"
version = "2.11.0"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
dependencies = [
{ name = "pydantic" },
{ name = "python-dotenv" },
{ name = "typing-inspection" },
]
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/d0e/87a1c7d33593b/pydantic_settings-2.11.0.tar.gz", hash = "sha256:d0e87a1c7d33593beb7194adb8470fc426e95ba02af83a0f23474a04c9a08180" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/fe2/cea3413b9530d/pydantic_settings-2.11.0-py3-none-any.whl", hash = "sha256:fe2cea3413b9530d10f3a5875adffb17ada5c1e1bab0b2885546d7310415207c" },
]
[[package]]
name = "pygments"
version = "2.19.2"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/636/cb2477cec7f89/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/865/40386c03d588b/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b" },
]
[[package]]
name = "python-dotenv"
version = "1.1.1"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/a8a/6399716257f45/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/31f/23644fe2602f8/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc" },
]
[[package]]
name = "python-multipart"
version = "0.0.20"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/8dd/0cab45b8e2306/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/8a6/2d3a8335e0658/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104" },
]
[[package]]
name = "referencing"
version = "0.37.0"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
dependencies = [
{ name = "attrs" },
{ name = "rpds-py" },
]
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/44a/efc3142c5b842/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/381/329a9f99628c9/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231" },
]
[[package]]
name = "rich"
version = "14.2.0"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
dependencies = [
{ name = "markdown-it-py" },
{ name = "pygments" },
]
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/73f/f50c7c0c1c77c/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/76b/c51fe2e57d2b1/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd" },
]
[[package]]
name = "rpds-py"
version = "0.27.1"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/26a/1c73171d10b7a/rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/e4b/9fcfbc0216338/rpds_py-0.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e4b9fcfbc021633863a37e92571d6f91851fa656f0180246e84cbd8b3f6b329b" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/144/1811a96eadca9/rpds_py-0.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1441811a96eadca93c517d08df75de45e5ffe68aa3089924f963c782c4b898cf" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/552/66dafa22e672f/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55266dafa22e672f5a4f65019015f90336ed31c6383bd53f5e7826d21a0e0b83" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/d78/827d7ac08627e/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78827d7ac08627ea2c8e02c9e5b41180ea5ea1f747e9db0915e3adf36b62dcf" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/ae9/2443798a40a92/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae92443798a40a92dc5f0b01d8a7c93adde0c4dc965310a29ae7c64d72b9fad2" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/c46/c9dd2403b66a2/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c46c9dd2403b66a2a3b9720ec4b74d4ab49d4fabf9f03dfdce2d42af913fe8d0" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/2ef/e4eb1d01b7f5f/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2efe4eb1d01b7f5f1939f4ef30ecea6c6b3521eec451fb93191bf84b2a522418" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/15d/3b4d83582d10c/rpds_py-0.27.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:15d3b4d83582d10c601f481eca29c3f138d44c92187d197aff663a269197c02d" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/4ed/2e16abbc982a1/rpds_py-0.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4ed2e16abbc982a169d30d1a420274a709949e2cbdef119fe2ec9d870b42f274" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/a75/f305c9b013289/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a75f305c9b013289121ec0f1181931975df78738cdf650093e6b86d74aa7d8dd" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/67c/e762070474588/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:67ce7620704745881a3d4b0ada80ab4d99df390838839921f99e63c474f82cf2" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/9d9/92ac10eb86d9b/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d992ac10eb86d9b6f369647b6a3f412fc0075cfd5d799530e84d335e440a002" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/4f7/5e4bd8ab8db62/rpds_py-0.27.1-cp313-cp313-win32.whl", hash = "sha256:4f75e4bd8ab8db624e02c8e2fc4063021b58becdbe6df793a8111d9343aec1e3" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/f90/25faafc62ed0b/rpds_py-0.27.1-cp313-cp313-win_amd64.whl", hash = "sha256:f9025faafc62ed0b75a53e541895ca272815bec18abe2249ff6501c8f2e12b83" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/ed1/0dc32829e7d22/rpds_py-0.27.1-cp313-cp313-win_arm64.whl", hash = "sha256:ed10dc32829e7d222b7d3b93136d25a406ba9788f6a7ebf6809092da1f4d279d" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/920/22bbbad0d4426/rpds_py-0.27.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:92022bbbad0d4426e616815b16bc4127f83c9a74940e1ccf3cfe0b387aba0228" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/471/62fdab9407ec3/rpds_py-0.27.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:47162fdab9407ec3f160805ac3e154df042e577dd53341745fc7fb3f625e6d92" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/fb8/9bec23fddc489/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb89bec23fddc489e5d78b550a7b773557c9ab58b7946154a10a6f7a214a48b2" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/e48/af21883ded2b3/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e48af21883ded2b3e9eb48cb7880ad8598b31ab752ff3be6457001d78f416723" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/6f5/b7bd8e219ed50/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f5b7bd8e219ed50299e58551a410b64daafb5017d54bbe822e003856f06a802" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/08f/1e20bccf73b08/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08f1e20bccf73b08d12d804d6e1c22ca5530e71659e6673bce31a6bb71c1e73f" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/0dc/5dceeaefcc96d/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dc5dceeaefcc96dc192e3a80bbe1d6c410c469e97bdd47494a7d930987f18b2" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/d76/f9cc8665acdc0/rpds_py-0.27.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:d76f9cc8665acdc0c9177043746775aa7babbf479b5520b78ae4002d889f5c21" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/134/fae0e36022eda/rpds_py-0.27.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:134fae0e36022edad8290a6661edf40c023562964efea0cc0ec7f5d392d2aaef" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/eb1/1a4f1b2b63337/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb11a4f1b2b63337cfd3b4d110af778a59aae51c81d195768e353d8b52f88081" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/13e/608ac9f50a0ed/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:13e608ac9f50a0ed4faec0e90ece76ae33b34c0e8656e3dceb9a7db994c692cd" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/dd2/135527aa40f06/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dd2135527aa40f061350c3f8f89da2644de26cd73e4de458e79606384f4f68e7" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/302/0724ade63fe32/rpds_py-0.27.1-cp313-cp313t-win32.whl", hash = "sha256:3020724ade63fe320a972e2ffd93b5623227e684315adce194941167fee02688" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/8ee/50c3e41739886/rpds_py-0.27.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8ee50c3e41739886606388ba3ab3ee2aae9f35fb23f833091833255a31740797" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/acb/9aafccaae278f/rpds_py-0.27.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:acb9aafccaae278f449d9c713b64a9e68662e7799dbd5859e2c6b3c67b56d334" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/b7f/b801aa7f845dd/rpds_py-0.27.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b7fb801aa7f845ddf601c49630deeeccde7ce10065561d92729bfe81bd21fb33" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/fe0/dd05afb46597b/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe0dd05afb46597b9a2e11c351e5e4283c741237e7f617ffb3252780cca9336a" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/b6d/fb0e058adb12d/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b6dfb0e058adb12d8b1d1b25f686e94ffa65d9995a5157afe99743bf7369d62b" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/ed0/90ccd235f6fa8/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed090ccd235f6fa8bb5861684567f0a83e04f52dfc2e5c05f2e4b1309fcf85e7" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/bf8/76e79763eecf3/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf876e79763eecf3e7356f157540d6a093cef395b65514f17a356f62af6cc136" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/12e/d005216a51b1d/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12ed005216a51b1d6e2b02a7bd31885fe317e45897de81d86dcce7d74618ffff" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/ee4/308f409a40e50/rpds_py-0.27.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ee4308f409a40e50593c7e3bb8cbe0b4d4c66d1674a316324f0c2f5383b486f9" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/0b0/8d152555acf1f/rpds_py-0.27.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b08d152555acf1f455154d498ca855618c1378ec810646fcd7c76416ac6dc60" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/dce/51c828941973a/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dce51c828941973a5684d458214d3a36fcd28da3e1875d659388f4f9f12cc33e" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/c14/76d6f29eb81aa/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c1476d6f29eb81aa4151c9a31219b03f1f798dc43d8af1250a870735516a1212" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/3ce/0cac322b0d69b/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3ce0cac322b0d69b63c9cdb895ee1b65805ec9ffad37639f291dd79467bee675" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/dfb/fac137d2a3d07/rpds_py-0.27.1-cp314-cp314-win32.whl", hash = "sha256:dfbfac137d2a3d0725758cd141f878bf4329ba25e34979797c89474a89a8a3a3" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/a6e/57b0abfe7cc51/rpds_py-0.27.1-cp314-cp314-win_amd64.whl", hash = "sha256:a6e57b0abfe7cc513450fcf529eb486b6e4d3f8aee83e92eb5f1ef848218d456" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/faf/8d146f3d476ab/rpds_py-0.27.1-cp314-cp314-win_arm64.whl", hash = "sha256:faf8d146f3d476abfee026c4ae3bdd9ca14236ae4e4c310cbd1cf75ba33d24a3" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/ba8/1d2b56b6d4911/rpds_py-0.27.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:ba81d2b56b6d4911ce735aad0a1d4495e808b8ee4dc58715998741a26874e7c2" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/84f/7d509870098de/rpds_py-0.27.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:84f7d509870098de0e864cad0102711c1e24e9b1a50ee713b65928adb22269e4" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/a9e/960fc78fecd11/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e960fc78fecd1100539f14132425e1d5fe44ecb9239f8f27f079962021523e" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/62f/85b665cedab1a/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62f85b665cedab1a503747617393573995dac4600ff51869d69ad2f39eb5e817" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/fed/467af29776f65/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fed467af29776f6556250c9ed85ea5a4dd121ab56a5f8b206e3e7a4c551e48ec" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/f27/29615f9d430af/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2729615f9d430af0ae6b36cf042cb55c0936408d543fb691e1a9e36648fd35a" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/1b2/07d881a9aef7b/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b207d881a9aef7ba753d69c123a35d96ca7cb808056998f6b9e8747321f03b8" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/639/fd5efec029f99/rpds_py-0.27.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:639fd5efec029f99b79ae47e5d7e00ad8a773da899b6309f6786ecaf22948c48" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/fec/c80cb2a90e28a/rpds_py-0.27.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fecc80cb2a90e28af8a9b366edacf33d7a91cbfe4c2c4544ea1246e949cfebeb" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/42a/89282d711711d/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42a89282d711711d0a62d6f57d81aa43a1368686c45bc1c46b7f079d55692734" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/cf9/931f14223de59/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:cf9931f14223de59551ab9d38ed18d92f14f055a5f78c1d8ad6493f735021bbb" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/f39/f58a27cc6e59f/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f39f58a27cc6e59f432b568ed8429c7e1641324fbe38131de852cd77b2d534b0" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/d5f/a0ee122dc09e2/rpds_py-0.27.1-cp314-cp314t-win32.whl", hash = "sha256:d5fa0ee122dc09e23607a28e6d7b150da16c662e66409bbe85230e4c85bb528a" },
{ url = "http://devpi.iepai.fun/root/pypi/+f/656/7d2bb951e2123/rpds_py-0.27.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6567d2bb951e21232c2f660c24cf3470bb96de56cdcb3f071a83feeaff8a2772" },
]
[[package]]
name = "shellingham"
version = "1.5.4"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/8db/ca0739d487e5b/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/7ec/fff8f2fd72616/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686" },
]
[[package]]
name = "sniffio"
version = "1.3.1"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/f43/24edc670a0f49/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/2f6/da418d1f1e0fd/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2" },
]
[[package]]
name = "sse-starlette"
version = "3.0.2"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
dependencies = [
{ name = "anyio" },
]
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/ccd/60b5765ebb358/sse_starlette-3.0.2.tar.gz", hash = "sha256:ccd60b5765ebb3584d0de2d7a6e4f745672581de4f5005ab31c3a25d10b52b3a" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/16b/7cbfddbcd4eac/sse_starlette-3.0.2-py3-none-any.whl", hash = "sha256:16b7cbfddbcd4eaca11f7b586f3b8a080f1afe952c15813455b162edea619e5a" },
]
[[package]]
name = "starlette"
version = "0.48.0"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
dependencies = [
{ name = "anyio" },
]
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/7e8/cee469a8ab235/starlette-0.48.0.tar.gz", hash = "sha256:7e8cee469a8ab2352911528110ce9088fdc6a37d9876926e73da7ce4aa4c7a46" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/076/4ca97b0975825/starlette-0.48.0-py3-none-any.whl", hash = "sha256:0764ca97b097582558ecb498132ed0c7d942f233f365b86ba37770e026510659" },
]
[[package]]
name = "typer"
version = "0.19.2"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
dependencies = [
{ name = "click" },
{ name = "rich" },
{ name = "shellingham" },
{ name = "typing-extensions" },
]
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/9ad/824308ded0ad0/typer-0.19.2.tar.gz", hash = "sha256:9ad824308ded0ad06cc716434705f691d4ee0bfd0fb081839d2e426860e7fdca" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/755/e7e19670ffad8/typer-0.19.2-py3-none-any.whl", hash = "sha256:755e7e19670ffad8283db353267cb81ef252f595aa6834a0d1ca9312d9326cb9" },
]
[[package]]
name = "typing-extensions"
version = "4.15.0"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/0ce/a48d173cc12fa/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/f0f/a19c6845758ab/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548" },
]
[[package]]
name = "typing-inspection"
version = "0.4.2"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/ba5/61c48a67c5958/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/4ed/1cacbdc298c22/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7" },
]
[[package]]
name = "uvicorn"
version = "0.37.0"
source = { registry = "http://devpi.iepai.fun/lzwc/dev/+simple/" }
dependencies = [
{ name = "click" },
{ name = "h11" },
]
sdist = { url = "http://devpi.iepai.fun/root/pypi/+f/411/5c8add6d3fd53/uvicorn-0.37.0.tar.gz", hash = "sha256:4115c8add6d3fd536c8ee77f0e14a7fd2ebba939fed9b02583a97f80648f9e13" }
wheels = [
{ url = "http://devpi.iepai.fun/root/pypi/+f/913/b2b8867234373/uvicorn-0.37.0-py3-none-any.whl", hash = "sha256:913b2b88672343739927ce381ff9e2ad62541f9f8289664fa1d1d3803fa2ce6c" },
]

View File

@@ -0,0 +1,12 @@
"""
Entry point for lzwcai-mcp-sqlexecutor
Runs the MCP server for SQL query execution
"""
import os
os.environ["databaseId"] = "19"
os.environ["backendBaseUrl"] = "http://192.168.11.24:8088"
if __name__ == "__main__":
# Import and run the actual MCP server
from lzwcai_mcpskills_analyzeWorkOrder.main import main
main()

View File

@@ -0,0 +1,35 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "lzwcai-mcpskills-analyzeWorkOrder"
version = "0.1.8"
description = "MCP server for executing business SQL queries with dynamic tool generation"
readme = "README.md"
requires-python = ">=3.13"
license = {text = "MIT"}
authors = [
{name = "lzwcai", email = "your-email@example.com"},
]
keywords = ["mcp", "sql", "executor", "server"]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.13",
]
dependencies = [
"httpx>=0.28.1",
"mcp[cli]>=1.10.1",
"pypinyin>=0.53.0",
]
[project.scripts]
lzwcai-mcpskills-analyzeWorkOrder = "lzwcai_mcpskills_analyzeWorkOrder.main:main"
[tool.hatch.build.targets.wheel]
packages = ["lzwcai_mcpskills_analyzeWorkOrder"]
[tool.hatch.build.targets.wheel.force-include]
"lzwcai_mcpskills_analyzeWorkOrder/businessQueries.json" = "lzwcai_mcpskills_analyzeWorkOrder/businessQueries.json"

80
tools_config.json Normal file
View File

@@ -0,0 +1,80 @@
[
{
"id": "2006300000000000000",
"createBy": "admin",
"createTime": "2026-01-02 10:00:00",
"updateBy": null,
"updateTime": null,
"serviceId": "2005848885546889218",
"uniqueName": "查询列表",
"name": "chaxunliebiao_x1y2z3w4",
"description": "查询所有工单列表返回fact_work_order表的全部数据",
"visualizable": 1,
"toolPrompt": "查询所有工单列表数据",
"toolType": "sql",
"datasourceId": "16",
"sqlTemplate": "SELECT * FROM fact_work_order",
"sqlParams": "{\"type\":\"object\",\"required\":[],\"properties\":{}}",
"resultType": "list",
"sourceType": "manual",
"trainingTaskId": null,
"tableMetadataIds": "",
"executionCount": 0,
"visualizationConfigs": null,
"inputJsonSchema": "",
"outputJsonSchema": "",
"lastExecutionTime": null
},
{
"id": "2006300000000000004",
"createBy": "admin",
"createTime": "2026-01-02 10:00:00",
"updateBy": null,
"updateTime": null,
"serviceId": "2005848885546889218",
"uniqueName": "工单详情",
"name": "gongdanxiangqing_m3n4o5p6",
"description": "根据工单编号查询工单详情,包括工单基本信息、产品信息、委外供应商、工艺路线等",
"visualizable": 1,
"toolPrompt": "输入工单编号查询工单详情,返回工单状态、数量、产品信息、供应商、工艺路线等",
"toolType": "sql",
"datasourceId": "16",
"sqlTemplate": "SELECT wo.work_order_id, wo.work_order_number AS 工单编号, wo.status AS 工单状态, wo.planned_qty AS 计划数量, wo.completed_qty AS 完成数量, wo.operation_good_qty AS 工序良品数, wo.operation_bad_qty AS 工序不良品数, wo.qc_good_qty AS 质检良品数, wo.qc_bad_qty AS 质检不良品数, wo.source_doc_number AS 来源单据编号, wo.event_time_utc AS 事件时间, p.product_code AS 产品编码, p.product_name AS 产品名称, p.product_spec AS 产品规格, s.supplier_name AS 委外供应商, r.routing_name AS 工艺路线 FROM fact_work_order wo LEFT JOIN dim_product p ON wo.product_id = p.product_id AND p.is_current = true LEFT JOIN dim_supplier s ON wo.supplier_id = s.supplier_id AND s.is_current = true LEFT JOIN dim_routing r ON wo.routing_id = r.routing_id AND r.is_current = true WHERE wo.work_order_number = {workOrderNumber}",
"sqlParams": "{\"type\":\"object\",\"required\":[\"workOrderNumber\"],\"properties\":{\"workOrderNumber\":{\"type\":\"string\",\"description\":\"工单编号\",\"examples\":[\"WO1311343859\"]}}}",
"resultType": "list",
"sourceType": "manual",
"trainingTaskId": null,
"tableMetadataIds": "",
"executionCount": 0,
"visualizationConfigs": null,
"inputJsonSchema": "",
"outputJsonSchema": "",
"lastExecutionTime": null
},
{
"id": "2006300000000000005",
"createBy": "admin",
"createTime": "2026-01-02 10:00:00",
"updateBy": null,
"updateTime": null,
"serviceId": "2005848885546889218",
"uniqueName": "工单执行进度与异常节点看板",
"name": "gongdanzhixingjinduyuyichangjiediankanban_q7r8s9t0",
"description": "工单执行进度与异常节点看板,展示工单完工进度、工序良率、质检良率、异常标识等关键监控指标",
"visualizable": 1,
"toolPrompt": "查看工单执行进度与异常节点看板,展示完工进度百分比、工序状态、报工统计、良率分析和异常标识",
"toolType": "sql",
"datasourceId": "16",
"sqlTemplate": "WITH work_order_progress AS (SELECT wo.work_order_number, wo.status AS work_order_status, wo.planned_qty, wo.completed_qty, wo.operation_good_qty, wo.operation_bad_qty, wo.qc_good_qty, wo.qc_bad_qty, wo.source_doc_number AS sales_order_number, wo.event_time_utc, p.product_code, p.product_name, CASE WHEN wo.planned_qty > 0 THEN ROUND(wo.completed_qty * 100.0 / wo.planned_qty, 2) ELSE 0 END AS completion_pct, CASE WHEN (wo.operation_good_qty + wo.operation_bad_qty) > 0 THEN ROUND(wo.operation_good_qty * 100.0 / (wo.operation_good_qty + wo.operation_bad_qty), 2) ELSE NULL END AS operation_yield_pct, CASE WHEN (wo.qc_good_qty + wo.qc_bad_qty) > 0 THEN ROUND(wo.qc_good_qty * 100.0 / (wo.qc_good_qty + wo.qc_bad_qty), 2) ELSE NULL END AS qc_yield_pct FROM fact_work_order wo LEFT JOIN dim_product p ON wo.product_id = p.product_id AND p.is_current = true), task_summary AS (SELECT work_order_number, COUNT(*) AS total_tasks, SUM(CASE WHEN actual_end_time_utc IS NOT NULL THEN 1 ELSE 0 END) AS completed_tasks, SUM(CASE WHEN actual_start_time_utc IS NOT NULL AND actual_end_time_utc IS NULL THEN 1 ELSE 0 END) AS in_progress_tasks, SUM(CASE WHEN actual_start_time_utc IS NULL THEN 1 ELSE 0 END) AS pending_tasks, SUM(bad_qty) AS total_bad_qty FROM fact_operation_task GROUP BY work_order_number), labor_summary AS (SELECT work_order_number, SUM(report_qty) AS total_report_qty, SUM(good_qty) AS total_good_qty, SUM(bad_qty) AS total_bad_qty, SUM(duration_minutes) AS total_work_minutes FROM fact_labor_report GROUP BY work_order_number), qc_summary AS (SELECT work_order_number, COUNT(*) AS qc_record_count, SUM(pass_qty) AS total_pass_qty, SUM(fail_qty) AS total_fail_qty FROM fact_quality_inspection WHERE work_order_number IS NOT NULL GROUP BY work_order_number) SELECT wp.work_order_number AS 工单编号, wp.product_code AS 产品编码, wp.product_name AS 产品名称, wp.sales_order_number AS 关联销售订单, wp.work_order_status AS 工单状态, wp.planned_qty AS 计划数量, wp.completed_qty AS 完成数量, wp.completion_pct AS 完工进度百分比, COALESCE(ts.total_tasks, 0) AS 工序总数, COALESCE(ts.completed_tasks, 0) AS 已完成工序, COALESCE(ts.in_progress_tasks, 0) AS 进行中工序, COALESCE(ts.pending_tasks, 0) AS 待开始工序, COALESCE(ls.total_report_qty, 0) AS 累计报工数, COALESCE(ls.total_work_minutes, 0) AS 累计工时_分钟, wp.operation_yield_pct AS 工序良率, wp.qc_yield_pct AS 质检良率, COALESCE(qc.total_pass_qty, 0) AS 质检通过数, COALESCE(qc.total_fail_qty, 0) AS 质检不通过数, CASE WHEN wp.completion_pct < 50 AND wp.work_order_status = 'STARTED' THEN '进度滞后' WHEN wp.operation_yield_pct < 90 THEN '工序良率异常' WHEN wp.qc_yield_pct < 95 THEN '质检良率异常' WHEN ts.total_bad_qty > 0 THEN '存在不良品' ELSE '正常' END AS 异常标识, wp.event_time_utc AS 最后更新时间 FROM work_order_progress wp LEFT JOIN task_summary ts ON wp.work_order_number = ts.work_order_number LEFT JOIN labor_summary ls ON wp.work_order_number = ls.work_order_number LEFT JOIN qc_summary qc ON wp.work_order_number = qc.work_order_number ORDER BY CASE wp.work_order_status WHEN 'STARTED' THEN 1 WHEN 'RELEASED' THEN 2 WHEN 'PLANNED' THEN 3 ELSE 4 END, wp.event_time_utc DESC",
"sqlParams": "{\"type\":\"object\",\"required\":[],\"properties\":{}}",
"resultType": "list",
"sourceType": "manual",
"trainingTaskId": null,
"tableMetadataIds": "",
"executionCount": 0,
"visualizationConfigs": "{\"chartType\":\"table\",\"highlightRules\":[{\"field\":\"异常标识\",\"condition\":\"notEquals\",\"value\":\"正常\",\"style\":\"danger\"}]}",
"inputJsonSchema": "",
"outputJsonSchema": "",
"lastExecutionTime": null
}
]