feat(lzwcai-demp-tool-server-dify-to-mcp): 初始化 Dify 集成工具模块
新增 Dify 到 MCP 的集成工具,支持通过 Dify API 将模型部署到 MCP 平台并进行推理。 该模块包含完整的服务器实现、依赖配置和命令行启动脚本。 主要功能: - 支持 Workflow 和 Completion 模式的调用 - 自动翻译工具名称为驼峰命名格式 - 提供文件上传与任务停止接口 - 兼容流式与非流式响应处理
This commit is contained in:
138
lzwcai_mcp_sqlexecutor/README.md
Normal file
138
lzwcai_mcp_sqlexecutor/README.md
Normal 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
|
||||
|
||||
10
lzwcai_mcp_sqlexecutor/lzwcai_mcp_sqlexecutor/.gitignore
vendored
Normal file
10
lzwcai_mcp_sqlexecutor/lzwcai_mcp_sqlexecutor/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# Python-generated files
|
||||
__pycache__/
|
||||
*.py[oc]
|
||||
build/
|
||||
dist/
|
||||
wheels/
|
||||
*.egg-info
|
||||
|
||||
# Virtual environments
|
||||
.venv
|
||||
@@ -0,0 +1 @@
|
||||
3.13
|
||||
154
lzwcai_mcp_sqlexecutor/lzwcai_mcp_sqlexecutor/README.md
Normal file
154
lzwcai_mcp_sqlexecutor/lzwcai_mcp_sqlexecutor/README.md
Normal 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
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
"""
|
||||
lzwcai-mcp-sqlexecutor - MCP server for executing business SQL queries
|
||||
"""
|
||||
|
||||
__version__ = "0.1.2"
|
||||
__author__ = "lzwcai"
|
||||
|
||||
__all__ = []
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
[]
|
||||
@@ -0,0 +1,51 @@
|
||||
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] - 调用第三方API,skill_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] - 调用第三方API,skill_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 工具
|
||||
@@ -0,0 +1,51 @@
|
||||
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] - 调用第三方API,skill_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] - 调用第三方API,skill_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 工具
|
||||
@@ -0,0 +1,36 @@
|
||||
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_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] - 调用第三方API,skill_id: 1981195682443014146
|
||||
2025-10-23 15:05:52 - mcp_services - INFO - [main.py:277] - 成功{'msg': '查询失败: 技能不存在: 1981195682443014146', 'code': 500}
|
||||
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 - 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_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] - 调用第三方API,skill_id: 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 - 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 工具
|
||||
358
lzwcai_mcp_sqlexecutor/lzwcai_mcp_sqlexecutor/main.py
Normal file
358
lzwcai_mcp_sqlexecutor/lzwcai_mcp_sqlexecutor/main.py
Normal 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_API
|
||||
# ================================
|
||||
|
||||
|
||||
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"调用第三方API,skill_id: {skill_id}")
|
||||
|
||||
# 获取原始数据
|
||||
raw_result = get_skill_by_id(skill_id)
|
||||
|
||||
mcp_logger.info(f"成功{raw_result}")
|
||||
|
||||
# 处理并返回
|
||||
processed_queries = process_skill_response(raw_result)
|
||||
|
||||
mcp_logger.info(f"成功获取并处理 {len(processed_queries)} 条数据")
|
||||
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()
|
||||
35
lzwcai_mcp_sqlexecutor/lzwcai_mcp_sqlexecutor/pyproject.toml
Normal file
35
lzwcai_mcp_sqlexecutor/lzwcai_mcp_sqlexecutor/pyproject.toml
Normal 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"
|
||||
@@ -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'
|
||||
]
|
||||
|
||||
@@ -0,0 +1,303 @@
|
||||
"""
|
||||
第三方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.debug(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.debug(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响应
|
||||
data = response.json()
|
||||
|
||||
logger.info(f"测试SQL API调用成功")
|
||||
logger.debug(f"响应数据: {json.dumps(data, ensure_ascii=False, indent=2)}")
|
||||
|
||||
return 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)}")
|
||||
|
||||
@@ -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时,必须将日志输出到stderr,stdout仅用于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
|
||||
@@ -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('-', '_')}"
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
# -*- 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 以避免修改原始数据
|
||||
input_schema = dict(parameters)
|
||||
|
||||
# 确保必需的字段存在
|
||||
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 验证通过"
|
||||
|
||||
497
lzwcai_mcp_sqlexecutor/lzwcai_mcp_sqlexecutor/uv.lock
generated
Normal file
497
lzwcai_mcp_sqlexecutor/lzwcai_mcp_sqlexecutor/uv.lock
generated
Normal 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" },
|
||||
]
|
||||
9
lzwcai_mcp_sqlexecutor/main.py
Normal file
9
lzwcai_mcp_sqlexecutor/main.py
Normal file
@@ -0,0 +1,9 @@
|
||||
"""
|
||||
Entry point for lzwcai-mcp-sqlexecutor
|
||||
Runs the MCP server for SQL query execution
|
||||
"""
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Import and run the actual MCP server
|
||||
from lzwcai_mcp_sqlexecutor.main import main
|
||||
main()
|
||||
35
lzwcai_mcp_sqlexecutor/pyproject.toml
Normal file
35
lzwcai_mcp_sqlexecutor/pyproject.toml
Normal file
@@ -0,0 +1,35 @@
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "lzwcai-mcp-sqlexecutor"
|
||||
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-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"
|
||||
Reference in New Issue
Block a user