Compare commits

...

10 Commits

Author SHA1 Message Date
fb61ae27cf feat: 新增HTML转URL工具服务,优化API转换器配置
- 新增lzwcai_mcpskills_visual2url项目,提供本地HTML文件、HTML代码转可访问URL的MCP工具
- 重构lzwcai_mcp_api_converter配置加载逻辑,取消内存缓存复用,强制拉取最新配置并新增缓存降级容错机制
- 升级lzwcai_mcp_api_converter至0.2.5、项目模板至0.1.3
- 更新各示例项目的环境配置参数与模板工具配置
2026-05-25 11:48:12 +08:00
992d97c0a4 feat(core): 改进API响应处理支持纯文本JSON解析
当API返回content-type为text/plain但实际内容是JSON格式时,
现在会尝试解析为JSON对象而不是直接返回文本,
提高API响应的兼容性

fix(create_mcp): 移除硬编码的输出schema定义

移除了工具注册中硬编码的outputSchema定义,
让系统使用更灵活的配置方式

chore(release): 更新版本号至0.2.4
2026-03-06 18:02:41 +08:00
8703a61198 feat(api-converter): 更新API配置并调整持久化令牌策略
- 替换api_config_9p04kww1pu.json为新的api_config_w8kgb73ib3.json配置文件,
  包含登录和单据查询两个API接口

- 修改AuthService类中的persist_token默认值为False,调整令牌持久化策略

- 移除旧的包信息文件、依赖文件和日志文件

- 更新API配置以支持金蝶K3Cloud系统的登录和单据查询功能
2026-03-06 16:19:07 +08:00
3c9fba36e9 feat(main): 修改 workflow extraContext 参数名称
将 workflow[extraContext] 参数名更改为 workflow_extraContext,
以避免方括号在某些系统中可能引起的解析问题。

同时更新了版本号从 0.1.7 到 0.1.8
2026-02-09 20:10:21 +08:00
32bc05376f feat(main): 修改工具输入schema以支持workflow[extraContext]字段
- 将params字段替换为workflow[extraContext]字段用于接收工作流额外上下文参数
- 更新描述信息,明确字段用途为接收工作流额外上下文参数(如环境变量等)
- 修改处理逻辑,提取workflow[extraContext]字段并合并到inputs中
- 当workflow[extraContext]为字典类型时合并到inputs,否则保留为独立字段
- 更新日志信息中的字段名称引用

chore(pyproject): 更新版本号至0.1.7
2026-02-09 19:44:51 +08:00
1b850913e7 feat(workflow): 添加 params 参数支持并优化工具调用处理
- 在输入 schema 中添加 params 字段,允许接收任何类型的额外参数
- 修改 handle_call_tool 函数,提取并合并 params 到 inputs
- 支持字典类型 params 的自动合并和其他类型 params 的保留
- 更新版本号从 0.1.5 到 0.1.6
2026-02-09 18:51:26 +08:00
a50aa307ab feat(schema-converter): 新增文件上传类型支持并优化schema转换逻辑
- 新增 file 和 fileList 类型用于单文件和多文件上传功能
- 添加文件配置信息处理,包括 accept、typeCategories、uploadMode 等参数
- 为文件类型添加 x-file-type、x-accept、x-type-categories 等扩展属性
- 优化默认值处理逻辑,排除文件类型设置默认值的情况

refactor(main): 重构工具配置处理优先级

- 将 schema 获取优先级调整为先从 sqlParams 转换,再使用 inputJsonSchema
- 增强错误处理机制,当 sqlParams 转换失败时自动回退到 inputJsonSchema
- 当两种方式都失败时提供空 schema 作为兜底方案
- 改进日志记录,增加调试信息和警告提示

chore: 更新项目版本号和测试配置

- 更新 pyproject.toml 中的版本号从 0.1.3 到 0.1.5
- 修改测试用的 workflowId 和 workflowExecuteKey 环境变量
2026-02-07 20:17:10 +08:00
e18c661368 feat(api-converter): 添加进销存采购订单API配置并实现本地缓存机制
新增api_config_9p04kww1pu.json配置文件,包含进销存采购订单相关的四个核心
API接口(查询列表、新建、详情、编辑),完善了load_api_configs函数,
增加本地文件缓存机制,支持从本地文件加载配置并在配置变更时同步保存,
优化refresh_api_configs函数以同步清理本地文件缓存。

BREAKING CHANGE: API配置方式调整,引入本地缓存机制可能影响原有部署流程
2026-02-07 15:48:01 +08:00
41c3d7a8fd fix(pyproject): 降低Python版本要求从3.13到3.10
修改了pyproject.toml文件中的Python版本要求,将最低支持版本从3.13降级到3.10,
同时更新了分类器中对应的Python版本声明,以提高项目的兼容性。
2026-01-28 09:36:17 +08:00
5107fdb74c chore(general): 更新项目配置文件
- 添加必要的配置项
- 优化现有设置
- 确保环境兼容性
2026-01-28 09:35:48 +08:00
70 changed files with 4427 additions and 416 deletions

View File

@@ -1,6 +1,6 @@
Metadata-Version: 2.4 Metadata-Version: 2.4
Name: lzwcai-mcp-api-converter Name: lzwcai-mcp-api-converter
Version: 0.2.0 Version: 0.2.5
Summary: 基于FastMCP框架的动态API工具服务器自动将企业业务API配置转换为MCP协议工具支持多种传输方式、企业认证和参数验证为AI助手提供标准化的业务接口访问能力。 Summary: 基于FastMCP框架的动态API工具服务器自动将企业业务API配置转换为MCP协议工具支持多种传输方式、企业认证和参数验证为AI助手提供标准化的业务接口访问能力。
Requires-Python: >=3.10 Requires-Python: >=3.10
Description-Content-Type: text/markdown Description-Content-Type: text/markdown

View File

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

View File

@@ -1 +0,0 @@
lzwc19781970385781825785858token={"authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiMTAwMDAwMDEiLCJsb2dpbl91c2VyX2tleSI6IjJmNmViMWVkYTk3MGRlNzI1OTM1YTczNzY5YWZmODJmZDE3MmFmMGIiLCJhYmJyIjoiXHU3MDc1XHU2Y2ZkXHU0ZTA3XHU1ZGRkIiwiYXVkIjoiIiwiZXhwIjoxNzY3MzQ4OTQxLCJpYXQiOjE3NjY3NDQxNDEsImlzcyI6IiIsImp0aSI6IjUyOTIyNzc0ZTdmZDA3MjZkNGEyY2FkMTgyYzEzNjM4IiwibmJmIjoxNzY2NzQ0MTQxLCJzdWIiOiIifQ.S8cvKtUfojJu0JvA1aPgd6H9y5ccd7XOa7UHMqZzn5w"}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,219 @@
{
"serverName": "lzwcai_mcp_api_converter",
"description": "登录、单据查询",
"domainUrl": "http://39.108.116.74",
"packageName": "lzwcai-mcp-dyntoolapi",
"version": "1.0.0",
"apiConfig": [
{
"id": "2029506334288154626",
"enterpriseId": "1932095424144715777",
"bizSysId": "2029468454441897985",
"domainUrl": "http://39.108.116.74",
"interfaceName": "登录",
"businessPrompts": "登录",
"returnType": "JSON",
"returnConversion": "{\"success_param\": \"code==200\", \"status_param\": \"code\", \"msg_param\": \"msg\", \"data\": \"data\"}",
"header": null,
"apiUrl": "/K3Cloud/Kingdee.BOS.WebApi.ServicesStub.AuthService.ValidateUser.common.kdsvc",
"parametersFormat": "2",
"method": "POST",
"status": 1,
"version": "1.0.0",
"authenticationRequired": 0,
"responseExample": null,
"crudType": "0",
"isView": 0,
"templateType": "markdown",
"viewTemplates": null,
"parameters": [
{
"keyParam": null,
"required": 1,
"paramName": "parameters",
"paramType": "ARRAY",
"paramPrompts": "",
"defaultValue": null,
"assoKey": null,
"assoApiId": null,
"memory": 0,
"apiId": "2029506334288154626",
"requestType": "body",
"dataFormat": null,
"validity": null,
"sort": "1",
"tags": "2",
"example": null
}
]
},
{
"id": "2029506334388817922",
"enterpriseId": "1932095424144715777",
"bizSysId": "2029468454441897985",
"domainUrl": "http://39.108.116.74",
"interfaceName": "单据查询",
"businessPrompts": "单据查询",
"returnType": "JSON",
"returnConversion": "{\"success_param\": \"code==200\", \"status_param\": \"code\", \"msg_param\": \"msg\", \"data\": \"data\"}",
"header": null,
"apiUrl": "/k3cloud/Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.ExecuteBillQuery.common.kdsvc",
"parametersFormat": "2",
"method": "POST",
"status": 1,
"version": "1.0.0",
"authenticationRequired": 1,
"responseExample": null,
"crudType": "4",
"isView": 0,
"templateType": "markdown",
"viewTemplates": null,
"parameters": [
{
"keyParam": null,
"required": 1,
"paramName": "parameters",
"paramType": "ARRAY",
"paramPrompts": "",
"defaultValue": null,
"assoKey": null,
"assoApiId": null,
"memory": 0,
"apiId": "2029506334388817922",
"requestType": "body",
"dataFormat": null,
"validity": null,
"sort": "1",
"tags": "2",
"example": null
},
{
"keyParam": null,
"required": 0,
"paramName": "parameters[].FormId",
"paramType": "STRING",
"paramPrompts": "",
"defaultValue": null,
"assoKey": null,
"assoApiId": null,
"memory": 0,
"apiId": "2029506334388817922",
"requestType": "body",
"dataFormat": null,
"validity": null,
"sort": "2",
"tags": "2",
"example": null
},
{
"keyParam": null,
"required": 0,
"paramName": "parameters[].TopRowCount",
"paramType": "INTEGER",
"paramPrompts": "",
"defaultValue": null,
"assoKey": null,
"assoApiId": null,
"memory": 0,
"apiId": "2029506334388817922",
"requestType": "body",
"dataFormat": null,
"validity": null,
"sort": "3",
"tags": "2",
"example": null
},
{
"keyParam": null,
"required": 0,
"paramName": "parameters[].Limit",
"paramType": "INTEGER",
"paramPrompts": "",
"defaultValue": null,
"assoKey": null,
"assoApiId": null,
"memory": 0,
"apiId": "2029506334388817922",
"requestType": "body",
"dataFormat": null,
"validity": null,
"sort": "4",
"tags": "1",
"example": null
},
{
"keyParam": null,
"required": 0,
"paramName": "parameters[].StartRow",
"paramType": "INTEGER",
"paramPrompts": "",
"defaultValue": null,
"assoKey": null,
"assoApiId": null,
"memory": 0,
"apiId": "2029506334388817922",
"requestType": "body",
"dataFormat": null,
"validity": null,
"sort": "5",
"tags": "2",
"example": null
},
{
"keyParam": null,
"required": 0,
"paramName": "parameters[].FilterString",
"paramType": "STRING",
"paramPrompts": "",
"defaultValue": null,
"assoKey": null,
"assoApiId": null,
"memory": 0,
"apiId": "2029506334388817922",
"requestType": "body",
"dataFormat": null,
"validity": null,
"sort": "6",
"tags": "2",
"example": null
},
{
"keyParam": null,
"required": 0,
"paramName": "parameters[].OrderString",
"paramType": "STRING",
"paramPrompts": "",
"defaultValue": null,
"assoKey": null,
"assoApiId": null,
"memory": 0,
"apiId": "2029506334388817922",
"requestType": "body",
"dataFormat": null,
"validity": null,
"sort": "7",
"tags": "3",
"example": null
},
{
"keyParam": null,
"required": 0,
"paramName": "parameters[].FieldKeys",
"paramType": "STRING",
"paramPrompts": "",
"defaultValue": null,
"assoKey": null,
"assoApiId": null,
"memory": 0,
"apiId": "2029506334388817922",
"requestType": "body",
"dataFormat": null,
"validity": null,
"sort": "8",
"tags": "0",
"example": null
}
]
}
]
}

View File

@@ -687,7 +687,7 @@ class AuthService:
self, self,
user_id: Optional[str], user_id: Optional[str],
biz_sys_id: Optional[str], biz_sys_id: Optional[str],
persist_token: bool = True, persist_token: bool = False,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
完整的请求鉴权处理 完整的请求鉴权处理
@@ -725,7 +725,7 @@ class AuthService:
user_id: Optional[str], user_id: Optional[str],
biz_sys_id: Optional[str], biz_sys_id: Optional[str],
token: Optional[str] = None, token: Optional[str] = None,
persist_token: bool = True, persist_token: bool = False,
) -> Optional[Union[str, Dict[str, Any]]]: ) -> Optional[Union[str, Dict[str, Any]]]:
""" """
检查用户Token是否有效如无效则重新获取 检查用户Token是否有效如无效则重新获取
@@ -760,7 +760,7 @@ class AuthService:
# 如果环境变量存在,直接返回值 # 如果环境变量存在,直接返回值
if exists: if exists:
self.logger.info( self.logger.info(
f"从环境变量获取到用户{user_id}业务系统{biz_sys_id}的Token" f"从环境变量获取到用户{user_id}业务系统{biz_sys_id}的Token: {token_value}"
) )
return token_value return token_value
@@ -774,7 +774,7 @@ class AuthService:
return await self._refresh_user_token(user_id, biz_sys_id, token_name, persist_token) return await self._refresh_user_token(user_id, biz_sys_id, token_name, persist_token)
async def _refresh_user_token( async def _refresh_user_token(
self, user_id: str, biz_sys_id: str, token_name: str, persist_token: bool = True self, user_id: str, biz_sys_id: str, token_name: str, persist_token: bool = False
) -> Optional[Union[str, Dict[str, Any]]]: ) -> Optional[Union[str, Dict[str, Any]]]:
"""刷新用户Token""" """刷新用户Token"""
# 获取鉴权类型和认证数据 # 获取鉴权类型和认证数据

View File

@@ -667,7 +667,9 @@ class ApiClient:
auth_result.get("error_response", {}).get("status_code"), auth_result.get("error_response", {}).get("status_code"),
) )
return auth_result.get("tokenHeader", {}) token_header = auth_result.get("tokenHeader", {})
logger.info(f"API调用获取到Token - 用户ID: {user_id}, 业务系统ID: {biz_sys_id}, Token: {token_header}")
return token_header
def _contains_file(self, data: Dict[str, Any]) -> bool: def _contains_file(self, data: Dict[str, Any]) -> bool:
@@ -938,10 +940,28 @@ class ApiClient:
"status_code": response.status_code, "status_code": response.status_code,
} }
else: else:
# 对于非JSON响应记录前100个字符 # 对于非JSON响应尝试解析为JSON某些API返回text/plain但内容是JSON
if not response.text:
return {
"status": "success",
"data": "",
"status_code": response.status_code,
}
response_preview = response.text[:100] + "..." if len(response.text) > 100 else response.text response_preview = response.text[:100] + "..." if len(response.text) > 100 else response.text
logger.info(f"文本响应预览: {response_preview}") logger.info(f"文本响应预览: {response_preview}")
# 尝试解析为JSON
text = response.text.strip()
if text and (text.startswith('{') or text.startswith('[')):
try:
json_response = json.loads(text)
logger.info("文本响应成功解析为JSON")
return json_response
except (json.JSONDecodeError, ValueError) as e:
logger.debug(f"文本响应JSON解析失败: {e}")
# 返回原始文本
return { return {
"status": "success", "status": "success",
"data": response.text, "data": response.text,

View File

@@ -138,13 +138,11 @@ def load_api_configs():
config_key = f"business{business_uuid}" config_key = f"business{business_uuid}"
logger.info(f"租户配置变量名: {config_key}") logger.info(f"租户配置变量名: {config_key}")
# 检查内存中是否已有该租户的配置 # 构建租户专属的配置文件路径
if config_key in business_configs: memory_config_path = os.path.join(current_dir, f"api_config_{business_uuid}.json")
logger.info(f"从内存中获取租户 {business_uuid} 的配置")
return business_configs[config_key]
# 内存中没有,从业务平台获取 # 本地文件不存在或加载失败,从业务平台获取
logger.info(f"内存中没有租户 {business_uuid}配置,开始从业务平台获取...") logger.info(f"准备从业务平台获取租户 {business_uuid}最新配置(强制刷新)...")
try: try:
# 从环境变量获取API ID列表 # 从环境变量获取API ID列表
@@ -181,11 +179,35 @@ def load_api_configs():
logger.info(f"配置已存储到内存变量: {config_key}") logger.info(f"配置已存储到内存变量: {config_key}")
logger.info(f"当前内存中共有 {len(business_configs)} 个租户配置") logger.info(f"当前内存中共有 {len(business_configs)} 个租户配置")
# 保存到本地文件作为备份
try:
logger.info(f"保存配置到本地文件: {memory_config_path}")
with open(memory_config_path, "w", encoding="utf-8") as f:
json.dump(config, f, ensure_ascii=False, indent=2)
logger.info("配置文件保存成功")
except Exception as save_error:
logger.warning(f"保存配置到本地文件失败(不影响运行): {str(save_error)}")
return config return config
except Exception as e: except Exception as e:
logger.error(f"获取业务API配置失败: {str(e)}") logger.error(f"获取业务API配置失败: {str(e)}")
error_msg = f"内存模式下无法获取租户 {business_uuid} 的配置: {str(e)}"
# 网络获取失败,尝试降级使用本地缓存
if os.path.exists(memory_config_path):
try:
logger.info(f"网络获取失败,尝试使用本地缓存文件: {memory_config_path}")
with open(memory_config_path, "r", encoding="utf-8") as f:
config = json.load(f)
logger.info(f"成功加载本地缓存配置,包含 {len(config.get('apiConfig', []))} 个API配置")
# 存储到内存中
business_configs[config_key] = config
return config
except Exception as cache_error:
logger.error(f"加载本地缓存也失败了: {str(cache_error)}")
error_msg = f"无法获取租户 {business_uuid} 的配置(网络和缓存均不可用): {str(e)}"
raise Exception(error_msg) raise Exception(error_msg)
# ==================== 模式二:文件模式(单租户) ==================== # ==================== 模式二:文件模式(单租户) ====================
@@ -367,24 +389,12 @@ class ApiToolPlugin(ToolPlugin):
tool_name = tool_config["interfaceName"] # 工具名称(拼音格式) tool_name = tool_config["interfaceName"] # 工具名称(拼音格式)
logger.debug(f"注册工具: {tool_name}") logger.debug(f"注册工具: {tool_name}")
# 定义输出 Schema先写死一个测试
output_schema = {
"type": "object",
"properties": {
"code": {"type": "integer", "description": "响应状态码0表示成功"},
"message": {"type": "string", "description": "响应消息"},
"data": {"type": "object", "description": "响应数据"}
},
"required": ["code", "message"]
}
# 创建MCP工具定义 # 创建MCP工具定义
tools.append( tools.append(
types.Tool( types.Tool(
name=tool_name, # 工具名称 name=tool_name,
description=tool_config["schema_description"], # 工具描述(包含参数说明) description=tool_config["schema_description"],
inputSchema=tool_config["schema"], # 输入参数的JSON Schema inputSchema=tool_config["schema"],
outputSchema=output_schema, # 输出参数的JSON Schema
) )
) )
@@ -639,7 +649,7 @@ def refresh_api_configs():
这个函数实现了配置的热加载功能,支持两种模式: 这个函数实现了配置的热加载功能,支持两种模式:
- 文件模式:当检测到配置文件变化时会被调用 - 文件模式:当检测到配置文件变化时会被调用
- 内存模式:强制重新从业务平台获取配置并更新内存 - 内存模式:强制重新从业务平台获取配置并更新内存和本地文件
全局变量更新: 全局变量更新:
- api_configs: 重新加载的API配置 - api_configs: 重新加载的API配置
@@ -658,7 +668,7 @@ def refresh_api_configs():
# 获取配置模式 # 获取配置模式
config_mode = os.getenv('configMode', 'memory').lower() config_mode = os.getenv('configMode', 'memory').lower()
# 内存模式下需要清除当前租户的缓存配置,强制重新获取 # 内存模式下需要清除当前租户的缓存配置和本地文件,强制重新获取
if config_mode == 'memory': if config_mode == 'memory':
business_uuid = os.getenv('businessUuid') business_uuid = os.getenv('businessUuid')
if business_uuid: if business_uuid:
@@ -667,6 +677,16 @@ def refresh_api_configs():
logger.info(f"清除租户 {business_uuid} 的缓存配置") logger.info(f"清除租户 {business_uuid} 的缓存配置")
del business_configs[config_key] del business_configs[config_key]
# 删除本地配置文件,强制从业务平台重新获取
current_dir = os.path.dirname(os.path.abspath(__file__))
memory_config_path = os.path.join(current_dir, f"api_config_{business_uuid}.json")
if os.path.exists(memory_config_path):
try:
os.remove(memory_config_path)
logger.info(f"已删除本地配置文件: {memory_config_path}")
except Exception as e:
logger.warning(f"删除本地配置文件失败: {str(e)}")
# 重新加载API配置 # 重新加载API配置
api_configs = load_api_configs() api_configs = load_api_configs()

View File

@@ -1,9 +1,9 @@
import os import os
os.environ["modelId"] = "1946471611735015425" os.environ["modelId"] = "1946471611735015425"
os.environ["bizSysId"] = "1970385781825785858" os.environ["bizSysId"] = "2029468454441897985"
os.environ["bizSysApiIds"] = "[\"1970386761072058369\",\"1970386761185304578\",\"1970386761583763457\",\"1970386761420185602\"]" os.environ["bizSysApiIds"] = "[\"2033382693160300546\"]"
os.environ["businessUuid"] = "u9ua9ss2l8c" os.environ["businessUuid"] = "dcqwlucfo7h"
os.environ["LZWCAI_CORP_MANAGER_URL"] = "http://192.168.2.236:8088" os.environ["LZWCAI_CORP_MANAGER_URL"] = "http://192.168.2.236:8088"
# 导入模块 # 导入模块
from lzwcai_mcp_api_converter.src.create_mcp import run_main from lzwcai_mcp_api_converter.src.create_mcp import run_main

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "lzwcai-mcp-api-converter" name = "lzwcai-mcp-api-converter"
version = "0.2.0" version = "0.2.5"
description = "基于FastMCP框架的动态API工具服务器自动将企业业务API配置转换为MCP协议工具支持多种传输方式、企业认证和参数验证为AI助手提供标准化的业务接口访问能力。" description = "基于FastMCP框架的动态API工具服务器自动将企业业务API配置转换为MCP协议工具支持多种传输方式、企业认证和参数验证为AI助手提供标准化的业务接口访问能力。"
readme = "README.md" readme = "README.md"
requires-python = ">=3.10" requires-python = ">=3.10"

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,223 @@
2025-12-31 12:57:27 - root - INFO - [logger_config.py:151] - 日志系统初始化完成 - 日志目录: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\logs
2025-12-31 12:57:27 - root - INFO - [logger_config.py:152] - 日志配置 - 级别: INFO, 文件大小限制: 10MB, 备份数量: 5
2025-12-31 12:57:27 - mcp_services - INFO - [main.py:347] - 开始运行 MCP SQL Executor 服务器
2025-12-31 12:57:27 - mcp_services - INFO - [main.py:299] - ============================================================
2025-12-31 12:57:27 - mcp_services - INFO - [main.py:300] - 正在启动 MCP 服务器: lzwcai-mcp-sqlexecutor
2025-12-31 12:57:27 - mcp_services - INFO - [main.py:301] - 版本: 0.1.0
2025-12-31 12:57:27 - mcp_services - INFO - [main.py:302] - ============================================================
2025-12-31 12:57:27 - mcp_services - INFO - [main.py:306] - 环境配置 - Database ID: 16
2025-12-31 12:57:27 - mcp_services - INFO - [main.py:307] - 环境配置 - Skill ID:
2025-12-31 12:57:27 - mcp_services - INFO - [main.py:308] - 环境配置 - Backend Base URL: http://192.168.11.24:8088
2025-12-31 12:57:27 - mcp_services - INFO - [main.py:309] - ============================================================
2025-12-31 12:57:27 - mcp_services - INFO - [main.py:314] - MCP 服务器已启动,等待客户端连接...
2025-12-31 12:57:28 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type ListToolsRequest
2025-12-31 12:57:28 - mcp_services - INFO - [main.py:156] - 收到列出工具请求
2025-12-31 12:57:28 - mcp_services - INFO - [main.py:119] - 初始化查询配置(数据源: api...
2025-12-31 12:57:28 - mcp_services - INFO - [main.py:278] - 调用第三方APIskill_id:
2025-12-31 12:57:28 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:71] - 正在调用API: http://192.168.11.24:8088/datasource/skill/getBySkillId/
2025-12-31 12:57:29 - httpx - INFO - [_client.py:1025] - HTTP Request: GET http://192.168.11.24:8088/datasource/skill/getBySkillId/ "HTTP/1.1 404 "
2025-12-31 12:57:29 - lzwcai_mcp_sqlexecutor.utils.api_client - ERROR - [api_client.py:97] - API请求失败 (HTTP 404): http://192.168.11.24:8088/datasource/skill/getBySkillId/
2025-12-31 12:57:29 - lzwcai_mcp_sqlexecutor.utils.api_client - ERROR - [api_client.py:98] - 错误响应: {"timestamp":"2025-12-31T12:57:30.248+08:00","status":404,"error":"Not Found","path":"/datasource/skill/getBySkillId/"}
2025-12-31 12:57:29 - mcp_services - ERROR - [main.py:292] - API调用失败: API请求失败 (HTTP 404): http://192.168.11.24:8088/datasource/skill/getBySkillId/
Traceback (most recent call last):
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 80, in get_skill_by_id
response.raise_for_status()
File "D:\anaconda3\Lib\site-packages\httpx\_models.py", line 829, in raise_for_status
raise HTTPStatusError(message, request=request, response=self)
httpx.HTTPStatusError: Client error '404 ' for url 'http://192.168.11.24:8088/datasource/skill/getBySkillId/'
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 281, in call_third_party_api
raw_result = get_skill_by_id(skill_id)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 208, in get_skill_by_id
return default_client.get_skill_by_id(skill_id)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 99, in get_skill_by_id
raise Exception(error_msg)
Exception: API请求失败 (HTTP 404): http://192.168.11.24:8088/datasource/skill/getBySkillId/
2025-12-31 12:57:29 - mcp_services - WARNING - [main.py:131] - API获取失败降级使用本地配置: API请求失败 (HTTP 404): http://192.168.11.24:8088/datasource/skill/getBySkillId/
2025-12-31 12:57:29 - mcp_services - INFO - [main.py:55] - 成功加载 3 个业务查询配置
2025-12-31 12:57:29 - mcp_services - ERROR - [main.py:92] - 生成工具模式失败: 2006300000000000001, 错误: 'businessName'
Traceback (most recent call last):
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
tool_name = query['businessName']
~~~~~^^^^^^^^^^^^^^^^
KeyError: 'businessName'
2025-12-31 12:57:29 - mcp_services - ERROR - [main.py:170] - 列出工具失败: 'businessName'
Traceback (most recent call last):
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 162, in handle_list_tools
tool = generate_tool_schema_from_query(query)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
tool_name = query['businessName']
~~~~~^^^^^^^^^^^^^^^^
KeyError: 'businessName'
2025-12-31 12:57:29 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type ListToolsRequest
2025-12-31 12:57:29 - mcp_services - INFO - [main.py:156] - 收到列出工具请求
2025-12-31 12:57:29 - mcp_services - ERROR - [main.py:92] - 生成工具模式失败: 2006300000000000001, 错误: 'businessName'
Traceback (most recent call last):
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
tool_name = query['businessName']
~~~~~^^^^^^^^^^^^^^^^
KeyError: 'businessName'
2025-12-31 12:57:29 - mcp_services - ERROR - [main.py:170] - 列出工具失败: 'businessName'
Traceback (most recent call last):
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 162, in handle_list_tools
tool = generate_tool_schema_from_query(query)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
tool_name = query['businessName']
~~~~~^^^^^^^^^^^^^^^^
KeyError: 'businessName'
2025-12-31 12:57:54 - root - INFO - [logger_config.py:151] - 日志系统初始化完成 - 日志目录: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\logs
2025-12-31 12:57:54 - root - INFO - [logger_config.py:152] - 日志配置 - 级别: INFO, 文件大小限制: 10MB, 备份数量: 5
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:347] - 开始运行 MCP SQL Executor 服务器
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:299] - ============================================================
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:300] - 正在启动 MCP 服务器: lzwcai-mcp-sqlexecutor
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:301] - 版本: 0.1.0
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:302] - ============================================================
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:306] - 环境配置 - Database ID: 16
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:307] - 环境配置 - Skill ID:
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:308] - 环境配置 - Backend Base URL: http://192.168.11.24:8088
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:309] - ============================================================
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:314] - MCP 服务器已启动,等待客户端连接...
2025-12-31 12:57:54 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type ListToolsRequest
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:156] - 收到列出工具请求
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:119] - 初始化查询配置(数据源: local...
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:55] - 成功加载 3 个业务查询配置
2025-12-31 12:57:54 - mcp_services - INFO - [main.py:123] - 本地配置: 3 条
2025-12-31 12:57:54 - mcp_services - ERROR - [main.py:92] - 生成工具模式失败: 2006300000000000001, 错误: 'businessName'
Traceback (most recent call last):
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
tool_name = query['businessName']
~~~~~^^^^^^^^^^^^^^^^
KeyError: 'businessName'
2025-12-31 12:57:54 - mcp_services - ERROR - [main.py:170] - 列出工具失败: 'businessName'
Traceback (most recent call last):
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 162, in handle_list_tools
tool = generate_tool_schema_from_query(query)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
tool_name = query['businessName']
~~~~~^^^^^^^^^^^^^^^^
KeyError: 'businessName'
2025-12-31 15:00:31 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type ListToolsRequest
2025-12-31 15:00:31 - mcp_services - INFO - [main.py:156] - 收到列出工具请求
2025-12-31 15:00:31 - mcp_services - ERROR - [main.py:92] - 生成工具模式失败: 2006300000000000001, 错误: 'businessName'
Traceback (most recent call last):
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
tool_name = query['businessName']
~~~~~^^^^^^^^^^^^^^^^
KeyError: 'businessName'
2025-12-31 15:00:31 - mcp_services - ERROR - [main.py:170] - 列出工具失败: 'businessName'
Traceback (most recent call last):
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 162, in handle_list_tools
tool = generate_tool_schema_from_query(query)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 80, in generate_tool_schema_from_query
tool_name = query['businessName']
~~~~~^^^^^^^^^^^^^^^^
KeyError: 'businessName'
2025-12-31 15:00:34 - mcp_services - INFO - [main.py:329] - MCP 服务器已关闭
2025-12-31 15:00:53 - root - INFO - [logger_config.py:151] - 日志系统初始化完成 - 日志目录: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\logs
2025-12-31 15:00:53 - root - INFO - [logger_config.py:152] - 日志配置 - 级别: INFO, 文件大小限制: 10MB, 备份数量: 5
2025-12-31 15:00:53 - mcp_services - INFO - [main.py:347] - 开始运行 MCP SQL Executor 服务器
2025-12-31 15:00:53 - mcp_services - INFO - [main.py:299] - ============================================================
2025-12-31 15:00:53 - mcp_services - INFO - [main.py:300] - 正在启动 MCP 服务器: lzwcai-mcp-sqlexecutor
2025-12-31 15:00:53 - mcp_services - INFO - [main.py:301] - 版本: 0.1.0
2025-12-31 15:00:53 - mcp_services - INFO - [main.py:302] - ============================================================
2025-12-31 15:00:53 - mcp_services - INFO - [main.py:306] - 环境配置 - Database ID: 29
2025-12-31 15:00:53 - mcp_services - INFO - [main.py:307] - 环境配置 - Skill ID:
2025-12-31 15:00:53 - mcp_services - INFO - [main.py:308] - 环境配置 - Backend Base URL: http://lzwcai-demp-corp-manager:8086
2025-12-31 15:00:53 - mcp_services - INFO - [main.py:309] - ============================================================
2025-12-31 15:00:53 - mcp_services - INFO - [main.py:314] - MCP 服务器已启动,等待客户端连接...
2025-12-31 15:00:54 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type ListToolsRequest
2025-12-31 15:00:54 - mcp_services - INFO - [main.py:156] - 收到列出工具请求
2025-12-31 15:00:54 - mcp_services - INFO - [main.py:119] - 初始化查询配置(数据源: api...
2025-12-31 15:00:54 - mcp_services - INFO - [main.py:278] - 调用第三方APIskill_id:
2025-12-31 15:00:54 - lzwcai_mcp_sqlexecutor.utils.api_client - INFO - [api_client.py:71] - 正在调用API: http://lzwcai-demp-corp-manager:8086/datasource/skill/getBySkillId/
2025-12-31 15:00:56 - lzwcai_mcp_sqlexecutor.utils.api_client - ERROR - [api_client.py:103] - API请求异常: http://lzwcai-demp-corp-manager:8086/datasource/skill/getBySkillId/, 错误: [Errno 11001] getaddrinfo failed
2025-12-31 15:00:56 - mcp_services - ERROR - [main.py:292] - API调用失败: API请求异常: http://lzwcai-demp-corp-manager:8086/datasource/skill/getBySkillId/, 错误: [Errno 11001] getaddrinfo failed
Traceback (most recent call last):
File "D:\anaconda3\Lib\site-packages\httpx\_transports\default.py", line 101, in map_httpcore_exceptions
yield
File "D:\anaconda3\Lib\site-packages\httpx\_transports\default.py", line 250, in handle_request
resp = self._pool.handle_request(req)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection_pool.py", line 216, in handle_request
raise exc from None
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection_pool.py", line 196, in handle_request
response = connection.handle_request(
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection.py", line 99, in handle_request
raise exc
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection.py", line 76, in handle_request
stream = self._connect(request)
^^^^^^^^^^^^^^^^^^^^^^
File "D:\anaconda3\Lib\site-packages\httpcore\_sync\connection.py", line 122, in _connect
stream = self._network_backend.connect_tcp(**kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\anaconda3\Lib\site-packages\httpcore\_backends\sync.py", line 205, in connect_tcp
with map_exceptions(exc_map):
^^^^^^^^^^^^^^^^^^^^^^^
File "D:\anaconda3\Lib\contextlib.py", line 158, in __exit__
self.gen.throw(value)
File "D:\anaconda3\Lib\site-packages\httpcore\_exceptions.py", line 14, in map_exceptions
raise to_exc(exc) from exc
httpcore.ConnectError: [Errno 11001] getaddrinfo failed
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 74, in get_skill_by_id
response = self.client.get(
^^^^^^^^^^^^^^^^
File "D:\anaconda3\Lib\site-packages\httpx\_client.py", line 1053, in get
return self.request(
^^^^^^^^^^^^^
File "D:\anaconda3\Lib\site-packages\httpx\_client.py", line 825, in request
return self.send(request, auth=auth, follow_redirects=follow_redirects)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\anaconda3\Lib\site-packages\httpx\_client.py", line 914, in send
response = self._send_handling_auth(
^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\anaconda3\Lib\site-packages\httpx\_client.py", line 942, in _send_handling_auth
response = self._send_handling_redirects(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\anaconda3\Lib\site-packages\httpx\_client.py", line 979, in _send_handling_redirects
response = self._send_single_request(request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\anaconda3\Lib\site-packages\httpx\_client.py", line 1014, in _send_single_request
response = transport.handle_request(request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\anaconda3\Lib\site-packages\httpx\_transports\default.py", line 249, in handle_request
with map_httpcore_exceptions():
^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\anaconda3\Lib\contextlib.py", line 158, in __exit__
self.gen.throw(value)
File "D:\anaconda3\Lib\site-packages\httpx\_transports\default.py", line 118, in map_httpcore_exceptions
raise mapped_exc(message) from exc
httpx.ConnectError: [Errno 11001] getaddrinfo failed
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\main.py", line 281, in call_third_party_api
raw_result = get_skill_by_id(skill_id)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 208, in get_skill_by_id
return default_client.get_skill_by_id(skill_id)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcp_sqlexecutor\lzwcai_mcp_sqlexecutor\utils\api_client.py", line 104, in get_skill_by_id
raise Exception(error_msg)
Exception: API请求异常: http://lzwcai-demp-corp-manager:8086/datasource/skill/getBySkillId/, 错误: [Errno 11001] getaddrinfo failed
2025-12-31 15:00:56 - mcp_services - WARNING - [main.py:131] - API获取失败降级使用本地配置: API请求异常: http://lzwcai-demp-corp-manager:8086/datasource/skill/getBySkillId/, 错误: [Errno 11001] getaddrinfo failed
2025-12-31 15:00:56 - mcp_services - INFO - [main.py:55] - 成功加载 0 个业务查询配置
2025-12-31 15:00:56 - mcp_services - INFO - [main.py:165] - 成功生成 0 个 MCP 工具
2025-12-31 15:00:56 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type ListToolsRequest
2025-12-31 15:00:56 - mcp_services - INFO - [main.py:156] - 收到列出工具请求
2025-12-31 15:00:56 - mcp_services - INFO - [main.py:165] - 成功生成 0 个 MCP 工具

File diff suppressed because one or more lines are too long

View File

@@ -7,7 +7,7 @@ name = "lzwcai-mcp-sqlexecutor"
version = "0.1.6" version = "0.1.6"
description = "MCP server for executing business SQL queries with dynamic tool generation" description = "MCP server for executing business SQL queries with dynamic tool generation"
readme = "README.md" readme = "README.md"
requires-python = ">=3.13" requires-python = ">=3.10"
license = {text = "MIT"} license = {text = "MIT"}
authors = [ authors = [
{name = "lzwcai", email = "your-email@example.com"}, {name = "lzwcai", email = "your-email@example.com"},
@@ -17,7 +17,7 @@ classifiers = [
"Development Status :: 3 - Alpha", "Development Status :: 3 - Alpha",
"Intended Audience :: Developers", "Intended Audience :: Developers",
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.10",
] ]
dependencies = [ dependencies = [
"httpx>=0.28.1", "httpx>=0.28.1",

View File

@@ -4,9 +4,9 @@ Runs the MCP server for SQL query execution
""" """
import os import os
os.environ["databaseId"] = "12" os.environ["databaseId"] = "162"
os.environ["skillId"] = "2013848312313335809" os.environ["skillId"] = "2008360664955854850"
os.environ["backendBaseUrl"] = "http://192.168.11.24:8088" os.environ["backendBaseUrl"] = "http://192.168.2.236:8088"
if __name__ == "__main__": if __name__ == "__main__":
# Import and run the actual MCP server # Import and run the actual MCP server
from lzwcai_mcp_sqlexecutor.main import main from lzwcai_mcp_sqlexecutor.main import main

View File

@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "lzwcai-mcp-sqlexecutor" name = "lzwcai-mcp-sqlexecutor"
version = "0.1.10" version = "0.1.11"
description = "MCP server for executing business SQL queries with dynamic tool generation" description = "MCP server for executing business SQL queries with dynamic tool generation"
readme = "README.md" readme = "README.md"
requires-python = ">=3.10" requires-python = ">=3.10"

File diff suppressed because one or more lines are too long

View File

@@ -240,3 +240,56 @@
2026-01-16 12:39:34 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: MetricTrendAndTurningPointWarning 2026-01-16 12:39:34 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: MetricTrendAndTurningPointWarning
2026-01-16 12:39:34 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API... 2026-01-16 12:39:34 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API...
2026-01-16 12:39:35 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功 2026-01-16 12:39:35 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功
2026-01-29 13:30:19 - mcp_services - INFO - [main.py:362] - 开始运行 MCP SQL Executor 服务器
2026-01-29 13:30:19 - mcp_services - INFO - [main.py:313] - ============================================================
2026-01-29 13:30:19 - mcp_services - INFO - [main.py:314] - 正在启动 MCP 服务器: lzwcai-mcpskills-analyzeOrder
2026-01-29 13:30:19 - mcp_services - INFO - [main.py:315] - 版本: 0.1.0
2026-01-29 13:30:19 - mcp_services - INFO - [main.py:316] - ============================================================
2026-01-29 13:30:19 - mcp_services - INFO - [main.py:320] - 环境配置 - Database ID: 57
2026-01-29 13:30:19 - mcp_services - INFO - [main.py:321] - 环境配置 - Datasource ID: 57
2026-01-29 13:30:19 - mcp_services - INFO - [main.py:322] - 环境配置 - Skill ID:
2026-01-29 13:30:19 - mcp_services - INFO - [main.py:323] - 环境配置 - Backend Base URL: http://192.167.30.2:8088
2026-01-29 13:30:19 - mcp_services - INFO - [main.py:324] - ============================================================
2026-01-29 13:30:19 - mcp_services - INFO - [main.py:329] - MCP 服务器已启动,等待客户端连接...
2026-01-29 13:30:21 - mcp_services - INFO - [main.py:156] - 收到列出工具请求
2026-01-29 13:30:21 - mcp_services - INFO - [main.py:119] - 初始化查询配置(数据源: local...
2026-01-29 13:30:21 - mcp_services - INFO - [main.py:55] - 成功加载 6 个业务查询配置
2026-01-29 13:30:21 - mcp_services - INFO - [main.py:123] - 本地配置: 6 条
2026-01-29 13:30:21 - mcp_services - INFO - [main.py:165] - 成功生成 6 个 MCP 工具
2026-01-29 13:30:22 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: OrderDelayWarningAnalysis
2026-01-29 13:30:22 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API...
2026-01-29 13:30:22 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功
2026-01-29 13:30:25 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: WorkOrderProgressAndAnomalyNodes
2026-01-29 13:30:25 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API...
2026-01-29 13:30:25 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功
2026-01-29 13:36:26 - mcp_services - INFO - [main.py:362] - 开始运行 MCP SQL Executor 服务器
2026-01-29 13:36:26 - mcp_services - INFO - [main.py:313] - ============================================================
2026-01-29 13:36:26 - mcp_services - INFO - [main.py:314] - 正在启动 MCP 服务器: lzwcai-mcpskills-analyzeOrder
2026-01-29 13:36:26 - mcp_services - INFO - [main.py:315] - 版本: 0.1.0
2026-01-29 13:36:26 - mcp_services - INFO - [main.py:316] - ============================================================
2026-01-29 13:36:26 - mcp_services - INFO - [main.py:320] - 环境配置 - Database ID: 57
2026-01-29 13:36:26 - mcp_services - INFO - [main.py:321] - 环境配置 - Datasource ID: 57
2026-01-29 13:36:26 - mcp_services - INFO - [main.py:322] - 环境配置 - Skill ID:
2026-01-29 13:36:26 - mcp_services - INFO - [main.py:323] - 环境配置 - Backend Base URL: http://192.167.30.2:8088
2026-01-29 13:36:26 - mcp_services - INFO - [main.py:324] - ============================================================
2026-01-29 13:36:26 - mcp_services - INFO - [main.py:329] - MCP 服务器已启动,等待客户端连接...
2026-01-29 13:36:27 - mcp_services - INFO - [main.py:156] - 收到列出工具请求
2026-01-29 13:36:27 - mcp_services - INFO - [main.py:119] - 初始化查询配置(数据源: local...
2026-01-29 13:36:27 - mcp_services - INFO - [main.py:55] - 成功加载 6 个业务查询配置
2026-01-29 13:36:27 - mcp_services - INFO - [main.py:123] - 本地配置: 6 条
2026-01-29 13:36:27 - mcp_services - INFO - [main.py:165] - 成功生成 6 个 MCP 工具
2026-01-29 13:36:28 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: OrderDelayWarningAnalysis
2026-01-29 13:36:28 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API...
2026-01-29 13:36:28 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功
2026-01-29 13:36:30 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: WorkOrderProgressAndAnomalyNodes
2026-01-29 13:36:30 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API...
2026-01-29 13:36:30 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功
2026-01-29 13:36:33 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: SupplyChainRiskWarning
2026-01-29 13:36:33 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API...
2026-01-29 13:36:34 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功
2026-01-29 13:36:38 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: EfficiencyOutputLossDashboard
2026-01-29 13:36:38 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API...
2026-01-29 13:36:38 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功
2026-01-29 13:36:41 - mcp_services - INFO - [main.py:190] - 收到工具调用请求: MetricTrendAndTurningPointWarning
2026-01-29 13:36:41 - mcp_services - INFO - [main.py:225] - 正在调用测试SQL API...
2026-01-29 13:36:41 - mcp_services - INFO - [main.py:227] - 测试SQL API调用成功

View File

@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "lzwcai-mcpskills-mfg-data-agent" name = "lzwcai-mcpskills-mfg-data-agent"
version = "0.1.4" version = "0.1.6"
description = "制造业数据智能体 - MCP server for manufacturing data intelligence with dynamic tool generation" description = "制造业数据智能体 - MCP server for manufacturing data intelligence with dynamic tool generation"
readme = "README.md" readme = "README.md"
requires-python = ">=3.10" requires-python = ">=3.10"

View File

@@ -0,0 +1,111 @@
import requests
import json
def main(**kwargs) -> dict:
"""
函数节点的入口函数
Args:
**kwargs: 从前端配置的参数传入,可通过变量引用获取工作流上下文
Returns:
dict: 返回结果将作为节点输出,可被后续节点引用
"""
# 从 kwargs 获取参数,如果没有提供则使用默认值
url = kwargs.get('url', 'http://192.167.30.2:8088/datasource/sqlExecutionLog/testBatchSqlWithSchema')
# 请求头从kwargs获取或使用默认值
authorization_token = kwargs.get('authorization_token', 'Bearer eyJhbGciOiJIUzUxMiJ9.eyJ0b2tlbl90eXBlIjoiTE9HSU4iLCJsb2dpbl91c2VyX2tleSI6IjAxNzZiNjEyLTc2YWItNDdhMS1iYTRiLTdjNWU2ZTMxNDlmZCJ9.w7aOfDJDHtA4bwNKIvUVK2cf1yO_2F27d_eYuos-p1-XGrSQOX0D4ny0b8Js36MhXwBnF4GDcy8V1VobEN6zBA')
headers = {
'Authorization': authorization_token
}
# 请求体参数从kwargs获取或使用默认值
payload_id = kwargs.get('id', '2006300000000000001')
business_name = kwargs.get('businessName', 'OrderDelayWarningAnalysis')
business_description = kwargs.get('businessDescription', '订单延迟预警分析:依据历史订单的生产周期、物流延误、设备故障等特征,输出延迟概率与红/黄/绿预警等级')
datasource_id = kwargs.get('datasourceId', '57')
sql_template = kwargs.get('sqlTemplate', 'WITH production_cycle_stats AS (SELECT COALESCE(AVG(GREATEST(0, EXTRACT(DAY FROM last_updated_utc - event_time_utc))), 0) AS avg_production_days FROM fact_work_order WHERE status = \'CLOSED\' AND last_updated_utc >= event_time_utc), logistics_delay_stats AS (SELECT customer_id, AVG(GREATEST(0, EXTRACT(DAY FROM event_time_utc - doc_date_utc))) AS avg_logistics_delay_days, SUM(CASE WHEN EXTRACT(DAY FROM event_time_utc - doc_date_utc) > 3 THEN 1 ELSE 0 END) AS delay_count FROM fact_sales_shipment WHERE doc_date_utc IS NOT NULL AND event_time_utc IS NOT NULL GROUP BY customer_id), quality_issue_stats AS (SELECT COALESCE(ROUND(SUM(fail_qty) * 100.0 / NULLIF(SUM(pass_qty + fail_qty), 0), 2), 0) AS defect_rate_pct FROM fact_quality_inspection WHERE pass_qty IS NOT NULL AND fail_qty IS NOT NULL), scrap_stats AS (SELECT COALESCE((SELECT COUNT(*) FROM fact_scrap) * 100.0 / NULLIF((SELECT COUNT(*) FROM fact_work_order WHERE status = \'CLOSED\'), 0), 0) AS scrap_rate_pct), active_work_order_risk AS (SELECT COUNT(*) AS active_wo_count, COALESCE(SUM(CASE WHEN planned_qty > 0 AND (completed_qty / planned_qty) 7 THEN 1 ELSE 0 END), 0) AS lagging_wo_count FROM fact_work_order WHERE status IN (\'OPEN\', \'STARTED\')), global_metrics AS (SELECT pcs.avg_production_days, qis.defect_rate_pct, ss.scrap_rate_pct, awr.active_wo_count, awr.lagging_wo_count FROM production_cycle_stats pcs, quality_issue_stats qis, scrap_stats ss, active_work_order_risk awr) SELECT so.sales_order_number AS order_number, c.customer_name, so.order_date_utc::DATE AS order_date, so.deal_amount AS order_amount, so.payment_status, ROUND(gm.avg_production_days::NUMERIC, 1) AS avg_production_days, ROUND(COALESCE(lds.avg_logistics_delay_days, 0)::NUMERIC, 1) AS avg_logistics_delay_days, COALESCE(lds.delay_count, 0)::INT AS historical_delay_count, ROUND(gm.defect_rate_pct::NUMERIC, 2) AS defect_rate_pct, ROUND(gm.scrap_rate_pct::NUMERIC, 2) AS scrap_rate_pct, gm.active_wo_count::INT AS active_work_order_count, gm.lagging_wo_count::INT AS lagging_work_order_count, ROUND(LEAST(100, GREATEST(0, LEAST(25, GREATEST(0, gm.avg_production_days - 10) * 2.5) + LEAST(30, COALESCE(lds.avg_logistics_delay_days, 0) * 6) + LEAST(25, gm.defect_rate_pct * 2.5) + LEAST(20, gm.lagging_wo_count * 10)))::NUMERIC, 1) AS delay_probability_pct, CASE WHEN (LEAST(25, GREATEST(0, gm.avg_production_days - 10) * 2.5) + LEAST(30, COALESCE(lds.avg_logistics_delay_days, 0) * 6) + LEAST(25, gm.defect_rate_pct * 2.5) + LEAST(20, gm.lagging_wo_count * 10)) >= 60 THEN \'RED\' WHEN (LEAST(25, GREATEST(0, gm.avg_production_days - 10) * 2.5) + LEAST(30, COALESCE(lds.avg_logistics_delay_days, 0) * 6) + LEAST(25, gm.defect_rate_pct * 2.5) + LEAST(20, gm.lagging_wo_count * 10)) >= 30 THEN \'YELLOW\' ELSE \'GREEN\' END AS warning_level, CASE WHEN gm.lagging_wo_count >= 2 THEN \'PRODUCTION_SEVERELY_DELAYED\' WHEN COALESCE(lds.avg_logistics_delay_days, 0) > 5 THEN \'HIGH_LOGISTICS_DELAY_RISK\' WHEN gm.avg_production_days > 15 THEN \'LONG_PRODUCTION_CYCLE\' WHEN gm.defect_rate_pct > 10 THEN \'QUALITY_ISSUES\' ELSE \'NORMAL\' END AS primary_risk_factor FROM fact_sales_order so LEFT JOIN dim_customer c ON so.customer_id = c.customer_id AND c.is_current = \'t\' CROSS JOIN global_metrics gm LEFT JOIN logistics_delay_stats lds ON so.customer_id = lds.customer_id WHERE EXTRACT(YEAR FROM so.order_date_utc) = 2025 ORDER BY delay_probability_pct DESC, so.order_date_utc DESC LIMIT 30')
parameters = kwargs.get('parameters', {})
# 请求体从kwargs获取或使用默认值
payload = {
"id": payload_id,
"businessName": business_name,
"businessDescription": business_description,
"datasourceId": datasource_id,
"sqlTemplate": sql_template,
"parameters": parameters
}
# 超时时间从kwargs获取或使用默认值
timeout = kwargs.get('timeout', 30)
try:
# 发送POST请求
response = requests.post(
url=url,
headers=headers,
json=payload,
timeout=timeout
)
# 检查HTTP响应状态码
response.raise_for_status()
# 解析响应结果
result = response.json()
# 返回接口调用结果
return {
"status": "success",
"status_code": response.status_code,
"result": result
}
except requests.exceptions.Timeout:
return {
"status": "error",
"error_type": "timeout",
"message": f"接口调用超时({timeout}秒)",
"result": None
}
except requests.exceptions.ConnectionError:
return {
"status": "error",
"error_type": "connection_error",
"message": "无法连接到目标服务器",
"result": None
}
except requests.exceptions.HTTPError as e:
return {
"status": "error",
"error_type": "http_error",
"status_code": response.status_code if 'response' in locals() else None,
"message": f"HTTP请求错误: {str(e)}",
"response_text": response.text if 'response' in locals() else "",
"result": None
}
except json.JSONDecodeError:
return {
"status": "error",
"error_type": "json_decode_error",
"message": "接口返回的不是有效的JSON格式",
"response_text": response.text if 'response' in locals() else "",
"result": None
}
except Exception as e:
return {
"status": "error",
"error_type": "unknown_error",
"message": f"接口调用失败: {str(e)}",
"result": None
}
if __name__ == "__main__":
result = main()
print(json.dumps(result, ensure_ascii=False, indent=2))

File diff suppressed because one or more lines are too long

View File

@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "lzwcai-mcpskills-mfg-data-agentv2" name = "lzwcai-mcpskills-mfg-data-agentv2"
version = "0.1.5" version = "0.1.6"
description = "制造业数据智能体 - MCP server for manufacturing data intelligence with dynamic tool generation" description = "制造业数据智能体 - MCP server for manufacturing data intelligence with dynamic tool generation"
readme = "README.md" readme = "README.md"
requires-python = ">=3.10" requires-python = ">=3.10"

View File

@@ -0,0 +1,65 @@
# lzwcai-mcpskills-template
MCP Server 模板项目,用于快速创建新的 MCP 服务。
## 功能
- 提供 MCP Server 基础框架
- 支持动态工具注册
- 内置日志系统
- 支持环境变量配置
## 安装
```bash
pip install lzwcai-mcpskills-template
```
## 使用
```bash
# 设置环境变量
export API_KEY="your-api-key"
export BASE_URL="http://your-api-server"
# 运行
lzwcai-mcpskills-template
```
## 环境变量
| 变量名 | 说明 | 默认值 |
|--------|------|--------|
| API_KEY | API密钥 | - |
| BASE_URL | 后端API地址 | http://localhost:8080 |
## 项目结构
```
lzwcai_mcpskills_template/
├── main.py # 入口文件
├── pyproject.toml # 项目配置
├── README.md # 说明文档
└── lzwcai_mcpskills_template/ # 核心代码
├── main.py # MCP Server 主逻辑
├── schema_converter.py # Schema 转换器
└── utils/ # 工具模块
├── __init__.py
├── api_client.py # API 客户端
├── env_config.py # 环境变量配置
├── json_helper.py # JSON 工具
└── logger_config.py # 日志配置
```
## 开发
基于此模板创建新项目:
1. 复制整个目录
2. 修改 `pyproject.toml` 中的项目名称
3. 修改 `lzwcai_mcpskills_template` 目录名
4.`main.py` 中实现你的工具逻辑
## License
MIT

View File

@@ -0,0 +1 @@
3.13

View File

@@ -0,0 +1,89 @@
2026-01-28 09:58:33 - root - INFO - [logger_config.py:112] - 日志系统初始化完成 - 日志目录: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcpskills_template\lzwcai_mcpskills_template\logs
2026-01-28 09:58:33 - mcp.server.lowlevel.server - DEBUG - [server.py:154] - Initializing server 'mcpskills_template_server'
2026-01-28 09:58:33 - mcp.server.lowlevel.server - DEBUG - [server.py:380] - Registering handler for ListToolsRequest
2026-01-28 09:58:33 - mcp.server.lowlevel.server - DEBUG - [server.py:441] - Registering handler for CallToolRequest
2026-01-28 09:58:33 - lzwcai_mcpskills_template.main - INFO - [main.py:304] - ==================================================
2026-01-28 09:58:33 - lzwcai_mcpskills_template.main - INFO - [main.py:305] - MCP Skills Template Server 启动
2026-01-28 09:58:33 - lzwcai_mcpskills_template.main - INFO - [main.py:306] - ==================================================
2026-01-28 09:58:33 - lzwcai_mcpskills_template.main - INFO - [main.py:311] - 命令行参数: {'mode': 'local', 'json_path': None}
2026-01-28 09:58:33 - lzwcai_mcpskills_template.main - INFO - [main.py:314] - 使用模式: local
2026-01-28 09:58:33 - lzwcai_mcpskills_template.main - INFO - [main.py:82] - 从本地加载 7 条配置: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcpskills_template\lzwcai_mcpskills_template\tools_config.json
2026-01-28 09:58:33 - lzwcai_mcpskills_template.main - INFO - [main.py:136] - 已加载 7 个工具配置
2026-01-28 09:58:33 - lzwcai_mcpskills_template.main - INFO - [main.py:321] - 开始运行 MCP Server (stdio 模式)
2026-01-28 09:58:33 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor
2026-01-28 09:58:33 - mcp.server.lowlevel.server - DEBUG - [server.py:582] - Received message: root=InitializedNotification(method='notifications/initialized', params=None, jsonrpc='2.0')
2026-01-28 09:58:34 - mcp.server.lowlevel.server - DEBUG - [server.py:582] - Received message: <mcp.shared.session.RequestResponder object at 0x0000029E6B64BFE0>
2026-01-28 09:58:34 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type ListToolsRequest
2026-01-28 09:58:34 - mcp.server.lowlevel.server - DEBUG - [server.py:621] - Dispatching request of type ListToolsRequest
2026-01-28 09:58:34 - lzwcai_mcpskills_template.main - INFO - [main.py:142] - 收到 ListTools 请求,当前配置数量: 7
2026-01-28 09:58:34 - lzwcai_mcpskills_template.main - INFO - [main.py:161] - ListTools 响应: 返回 7 个工具
2026-01-28 09:58:34 - mcp.server.lowlevel.server - DEBUG - [server.py:662] - Response sent
2026-01-28 09:58:36 - mcp.server.lowlevel.server - DEBUG - [server.py:582] - Received message: <mcp.shared.session.RequestResponder object at 0x0000029E6B59CFE0>
2026-01-28 09:58:36 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest
2026-01-28 09:58:36 - mcp.server.lowlevel.server - DEBUG - [server.py:621] - Dispatching request of type CallToolRequest
2026-01-28 09:58:36 - lzwcai_mcpskills_template.main - INFO - [main.py:171] - 收到 CallTool 请求: name=demo_mixed, arguments=None
2026-01-28 09:58:36 - lzwcai_mcpskills_template.main - INFO - [main.py:187] - 工具执行成功: demo_mixed
2026-01-28 09:58:36 - mcp.server.lowlevel.server - DEBUG - [server.py:662] - Response sent
2026-01-28 09:58:55 - mcp.server.lowlevel.server - DEBUG - [server.py:582] - Received message: <mcp.shared.session.RequestResponder object at 0x0000029E6B39D7C0>
2026-01-28 09:58:55 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest
2026-01-28 09:58:55 - mcp.server.lowlevel.server - DEBUG - [server.py:621] - Dispatching request of type CallToolRequest
2026-01-28 09:58:55 - lzwcai_mcpskills_template.main - INFO - [main.py:171] - 收到 CallTool 请求: name=demo_error, arguments=None
2026-01-28 09:58:55 - lzwcai_mcpskills_template.main - ERROR - [main.py:190] - 工具执行失败: 这是一个演示用的模拟错误!执行被中断。
Traceback (most recent call last):
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcpskills_template\lzwcai_mcpskills_template\main.py", line 186, in handle_call_tool
result_contents = execute_tool(name, arguments or {}, tool_config)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcpskills_template\lzwcai_mcpskills_template\main.py", line 259, in execute_tool
raise ValueError("这是一个演示用的模拟错误!执行被中断。")
ValueError: 这是一个演示用的模拟错误!执行被中断。
2026-01-28 09:58:55 - mcp.server.lowlevel.server - DEBUG - [server.py:662] - Response sent
2026-01-28 09:59:01 - mcp.server.lowlevel.server - DEBUG - [server.py:582] - Received message: <mcp.shared.session.RequestResponder object at 0x0000029E6B6164E0>
2026-01-28 09:59:01 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest
2026-01-28 09:59:01 - mcp.server.lowlevel.server - DEBUG - [server.py:621] - Dispatching request of type CallToolRequest
2026-01-28 09:59:01 - lzwcai_mcpskills_template.main - INFO - [main.py:171] - 收到 CallTool 请求: name=demo_resource, arguments=None
2026-01-28 09:59:01 - lzwcai_mcpskills_template.main - INFO - [main.py:187] - 工具执行成功: demo_resource
2026-01-28 09:59:01 - mcp.server.lowlevel.server - DEBUG - [server.py:662] - Response sent
2026-01-28 09:59:04 - mcp.server.lowlevel.server - DEBUG - [server.py:582] - Received message: <mcp.shared.session.RequestResponder object at 0x0000029E6B64BF80>
2026-01-28 09:59:04 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest
2026-01-28 09:59:04 - mcp.server.lowlevel.server - DEBUG - [server.py:621] - Dispatching request of type CallToolRequest
2026-01-28 09:59:04 - lzwcai_mcpskills_template.main - INFO - [main.py:171] - 收到 CallTool 请求: name=demo_image, arguments=None
2026-01-28 09:59:04 - lzwcai_mcpskills_template.main - INFO - [main.py:187] - 工具执行成功: demo_image
2026-01-28 09:59:04 - mcp.server.lowlevel.server - DEBUG - [server.py:662] - Response sent
2026-01-28 09:59:09 - mcp.server.lowlevel.server - DEBUG - [server.py:582] - Received message: <mcp.shared.session.RequestResponder object at 0x0000029E6B36DD90>
2026-01-28 09:59:09 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest
2026-01-28 09:59:09 - mcp.server.lowlevel.server - DEBUG - [server.py:621] - Dispatching request of type CallToolRequest
2026-01-28 09:59:09 - lzwcai_mcpskills_template.main - INFO - [main.py:171] - 收到 CallTool 请求: name=demo_image, arguments=None
2026-01-28 09:59:09 - lzwcai_mcpskills_template.main - INFO - [main.py:187] - 工具执行成功: demo_image
2026-01-28 09:59:09 - mcp.server.lowlevel.server - DEBUG - [server.py:662] - Response sent
2026-01-28 10:02:13 - root - INFO - [logger_config.py:112] - 日志系统初始化完成 - 日志目录: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcpskills_template\lzwcai_mcpskills_template\logs
2026-01-28 10:02:13 - mcp.server.lowlevel.server - DEBUG - [server.py:154] - Initializing server 'mcpskills_template_server'
2026-01-28 10:02:13 - mcp.server.lowlevel.server - DEBUG - [server.py:380] - Registering handler for ListToolsRequest
2026-01-28 10:02:13 - mcp.server.lowlevel.server - DEBUG - [server.py:441] - Registering handler for CallToolRequest
2026-01-28 10:02:13 - lzwcai_mcpskills_template.main - INFO - [main.py:322] - ==================================================
2026-01-28 10:02:13 - lzwcai_mcpskills_template.main - INFO - [main.py:323] - MCP Skills Template Server 启动
2026-01-28 10:02:13 - lzwcai_mcpskills_template.main - INFO - [main.py:324] - ==================================================
2026-01-28 10:02:13 - lzwcai_mcpskills_template.main - INFO - [main.py:329] - 命令行参数: {'mode': 'local', 'json_path': None}
2026-01-28 10:02:13 - lzwcai_mcpskills_template.main - INFO - [main.py:332] - 使用模式: local
2026-01-28 10:02:13 - lzwcai_mcpskills_template.main - INFO - [main.py:83] - 从本地加载 8 条配置: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcpskills_template\lzwcai_mcpskills_template\tools_config.json
2026-01-28 10:02:13 - lzwcai_mcpskills_template.main - INFO - [main.py:137] - 已加载 8 个工具配置
2026-01-28 10:02:13 - lzwcai_mcpskills_template.main - INFO - [main.py:339] - 开始运行 MCP Server (stdio 模式)
2026-01-28 10:02:13 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor
2026-01-28 10:02:13 - mcp.server.lowlevel.server - DEBUG - [server.py:582] - Received message: root=InitializedNotification(method='notifications/initialized', params=None, jsonrpc='2.0')
2026-01-28 10:02:40 - mcp.server.lowlevel.server - DEBUG - [server.py:582] - Received message: <mcp.shared.session.RequestResponder object at 0x000001407A58E3F0>
2026-01-28 10:02:40 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type ListToolsRequest
2026-01-28 10:02:40 - mcp.server.lowlevel.server - DEBUG - [server.py:621] - Dispatching request of type ListToolsRequest
2026-01-28 10:02:40 - lzwcai_mcpskills_template.main - INFO - [main.py:143] - 收到 ListTools 请求,当前配置数量: 8
2026-01-28 10:02:40 - lzwcai_mcpskills_template.main - INFO - [main.py:162] - ListTools 响应: 返回 8 个工具
2026-01-28 10:02:40 - mcp.server.lowlevel.server - DEBUG - [server.py:662] - Response sent
2026-01-28 10:02:44 - mcp.server.lowlevel.server - DEBUG - [server.py:582] - Received message: <mcp.shared.session.RequestResponder object at 0x000001407A66FAD0>
2026-01-28 10:02:44 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest
2026-01-28 10:02:44 - mcp.server.lowlevel.server - DEBUG - [server.py:621] - Dispatching request of type CallToolRequest
2026-01-28 10:02:44 - lzwcai_mcpskills_template.main - INFO - [main.py:172] - 收到 CallTool 请求: name=demo_complex, arguments={"user_info": {}}
2026-01-28 10:02:44 - lzwcai_mcpskills_template.main - INFO - [main.py:189] - 工具执行成功: demo_complex
2026-01-28 10:02:44 - mcp.server.lowlevel.server - DEBUG - [server.py:662] - Response sent
2026-01-28 10:02:50 - mcp.server.lowlevel.server - DEBUG - [server.py:582] - Received message: <mcp.shared.session.RequestResponder object at 0x000001407A1DAF00>
2026-01-28 10:02:50 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest
2026-01-28 10:02:50 - mcp.server.lowlevel.server - DEBUG - [server.py:621] - Dispatching request of type CallToolRequest
2026-01-28 10:02:50 - lzwcai_mcpskills_template.main - INFO - [main.py:172] - 收到 CallTool 请求: name=demo_mixed, arguments=None
2026-01-28 10:02:50 - lzwcai_mcpskills_template.main - INFO - [main.py:189] - 工具执行成功: demo_mixed
2026-01-28 10:02:50 - mcp.server.lowlevel.server - DEBUG - [server.py:662] - Response sent

View File

@@ -0,0 +1,89 @@
2026-01-28 09:58:33 - root - INFO - [logger_config.py:112] - 日志系统初始化完成 - 日志目录: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcpskills_template\lzwcai_mcpskills_template\logs
2026-01-28 09:58:33 - mcp.server.lowlevel.server - DEBUG - [server.py:154] - Initializing server 'mcpskills_template_server'
2026-01-28 09:58:33 - mcp.server.lowlevel.server - DEBUG - [server.py:380] - Registering handler for ListToolsRequest
2026-01-28 09:58:33 - mcp.server.lowlevel.server - DEBUG - [server.py:441] - Registering handler for CallToolRequest
2026-01-28 09:58:33 - lzwcai_mcpskills_template.main - INFO - [main.py:304] - ==================================================
2026-01-28 09:58:33 - lzwcai_mcpskills_template.main - INFO - [main.py:305] - MCP Skills Template Server 启动
2026-01-28 09:58:33 - lzwcai_mcpskills_template.main - INFO - [main.py:306] - ==================================================
2026-01-28 09:58:33 - lzwcai_mcpskills_template.main - INFO - [main.py:311] - 命令行参数: {'mode': 'local', 'json_path': None}
2026-01-28 09:58:33 - lzwcai_mcpskills_template.main - INFO - [main.py:314] - 使用模式: local
2026-01-28 09:58:33 - lzwcai_mcpskills_template.main - INFO - [main.py:82] - 从本地加载 7 条配置: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcpskills_template\lzwcai_mcpskills_template\tools_config.json
2026-01-28 09:58:33 - lzwcai_mcpskills_template.main - INFO - [main.py:136] - 已加载 7 个工具配置
2026-01-28 09:58:33 - lzwcai_mcpskills_template.main - INFO - [main.py:321] - 开始运行 MCP Server (stdio 模式)
2026-01-28 09:58:33 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor
2026-01-28 09:58:33 - mcp.server.lowlevel.server - DEBUG - [server.py:582] - Received message: root=InitializedNotification(method='notifications/initialized', params=None, jsonrpc='2.0')
2026-01-28 09:58:34 - mcp.server.lowlevel.server - DEBUG - [server.py:582] - Received message: <mcp.shared.session.RequestResponder object at 0x0000029E6B64BFE0>
2026-01-28 09:58:34 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type ListToolsRequest
2026-01-28 09:58:34 - mcp.server.lowlevel.server - DEBUG - [server.py:621] - Dispatching request of type ListToolsRequest
2026-01-28 09:58:34 - lzwcai_mcpskills_template.main - INFO - [main.py:142] - 收到 ListTools 请求,当前配置数量: 7
2026-01-28 09:58:34 - lzwcai_mcpskills_template.main - INFO - [main.py:161] - ListTools 响应: 返回 7 个工具
2026-01-28 09:58:34 - mcp.server.lowlevel.server - DEBUG - [server.py:662] - Response sent
2026-01-28 09:58:36 - mcp.server.lowlevel.server - DEBUG - [server.py:582] - Received message: <mcp.shared.session.RequestResponder object at 0x0000029E6B59CFE0>
2026-01-28 09:58:36 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest
2026-01-28 09:58:36 - mcp.server.lowlevel.server - DEBUG - [server.py:621] - Dispatching request of type CallToolRequest
2026-01-28 09:58:36 - lzwcai_mcpskills_template.main - INFO - [main.py:171] - 收到 CallTool 请求: name=demo_mixed, arguments=None
2026-01-28 09:58:36 - lzwcai_mcpskills_template.main - INFO - [main.py:187] - 工具执行成功: demo_mixed
2026-01-28 09:58:36 - mcp.server.lowlevel.server - DEBUG - [server.py:662] - Response sent
2026-01-28 09:58:55 - mcp.server.lowlevel.server - DEBUG - [server.py:582] - Received message: <mcp.shared.session.RequestResponder object at 0x0000029E6B39D7C0>
2026-01-28 09:58:55 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest
2026-01-28 09:58:55 - mcp.server.lowlevel.server - DEBUG - [server.py:621] - Dispatching request of type CallToolRequest
2026-01-28 09:58:55 - lzwcai_mcpskills_template.main - INFO - [main.py:171] - 收到 CallTool 请求: name=demo_error, arguments=None
2026-01-28 09:58:55 - lzwcai_mcpskills_template.main - ERROR - [main.py:190] - 工具执行失败: 这是一个演示用的模拟错误!执行被中断。
Traceback (most recent call last):
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcpskills_template\lzwcai_mcpskills_template\main.py", line 186, in handle_call_tool
result_contents = execute_tool(name, arguments or {}, tool_config)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcpskills_template\lzwcai_mcpskills_template\main.py", line 259, in execute_tool
raise ValueError("这是一个演示用的模拟错误!执行被中断。")
ValueError: 这是一个演示用的模拟错误!执行被中断。
2026-01-28 09:58:55 - mcp.server.lowlevel.server - DEBUG - [server.py:662] - Response sent
2026-01-28 09:59:01 - mcp.server.lowlevel.server - DEBUG - [server.py:582] - Received message: <mcp.shared.session.RequestResponder object at 0x0000029E6B6164E0>
2026-01-28 09:59:01 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest
2026-01-28 09:59:01 - mcp.server.lowlevel.server - DEBUG - [server.py:621] - Dispatching request of type CallToolRequest
2026-01-28 09:59:01 - lzwcai_mcpskills_template.main - INFO - [main.py:171] - 收到 CallTool 请求: name=demo_resource, arguments=None
2026-01-28 09:59:01 - lzwcai_mcpskills_template.main - INFO - [main.py:187] - 工具执行成功: demo_resource
2026-01-28 09:59:01 - mcp.server.lowlevel.server - DEBUG - [server.py:662] - Response sent
2026-01-28 09:59:04 - mcp.server.lowlevel.server - DEBUG - [server.py:582] - Received message: <mcp.shared.session.RequestResponder object at 0x0000029E6B64BF80>
2026-01-28 09:59:04 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest
2026-01-28 09:59:04 - mcp.server.lowlevel.server - DEBUG - [server.py:621] - Dispatching request of type CallToolRequest
2026-01-28 09:59:04 - lzwcai_mcpskills_template.main - INFO - [main.py:171] - 收到 CallTool 请求: name=demo_image, arguments=None
2026-01-28 09:59:04 - lzwcai_mcpskills_template.main - INFO - [main.py:187] - 工具执行成功: demo_image
2026-01-28 09:59:04 - mcp.server.lowlevel.server - DEBUG - [server.py:662] - Response sent
2026-01-28 09:59:09 - mcp.server.lowlevel.server - DEBUG - [server.py:582] - Received message: <mcp.shared.session.RequestResponder object at 0x0000029E6B36DD90>
2026-01-28 09:59:09 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest
2026-01-28 09:59:09 - mcp.server.lowlevel.server - DEBUG - [server.py:621] - Dispatching request of type CallToolRequest
2026-01-28 09:59:09 - lzwcai_mcpskills_template.main - INFO - [main.py:171] - 收到 CallTool 请求: name=demo_image, arguments=None
2026-01-28 09:59:09 - lzwcai_mcpskills_template.main - INFO - [main.py:187] - 工具执行成功: demo_image
2026-01-28 09:59:09 - mcp.server.lowlevel.server - DEBUG - [server.py:662] - Response sent
2026-01-28 10:02:13 - root - INFO - [logger_config.py:112] - 日志系统初始化完成 - 日志目录: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcpskills_template\lzwcai_mcpskills_template\logs
2026-01-28 10:02:13 - mcp.server.lowlevel.server - DEBUG - [server.py:154] - Initializing server 'mcpskills_template_server'
2026-01-28 10:02:13 - mcp.server.lowlevel.server - DEBUG - [server.py:380] - Registering handler for ListToolsRequest
2026-01-28 10:02:13 - mcp.server.lowlevel.server - DEBUG - [server.py:441] - Registering handler for CallToolRequest
2026-01-28 10:02:13 - lzwcai_mcpskills_template.main - INFO - [main.py:322] - ==================================================
2026-01-28 10:02:13 - lzwcai_mcpskills_template.main - INFO - [main.py:323] - MCP Skills Template Server 启动
2026-01-28 10:02:13 - lzwcai_mcpskills_template.main - INFO - [main.py:324] - ==================================================
2026-01-28 10:02:13 - lzwcai_mcpskills_template.main - INFO - [main.py:329] - 命令行参数: {'mode': 'local', 'json_path': None}
2026-01-28 10:02:13 - lzwcai_mcpskills_template.main - INFO - [main.py:332] - 使用模式: local
2026-01-28 10:02:13 - lzwcai_mcpskills_template.main - INFO - [main.py:83] - 从本地加载 8 条配置: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcpskills_template\lzwcai_mcpskills_template\tools_config.json
2026-01-28 10:02:13 - lzwcai_mcpskills_template.main - INFO - [main.py:137] - 已加载 8 个工具配置
2026-01-28 10:02:13 - lzwcai_mcpskills_template.main - INFO - [main.py:339] - 开始运行 MCP Server (stdio 模式)
2026-01-28 10:02:13 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor
2026-01-28 10:02:13 - mcp.server.lowlevel.server - DEBUG - [server.py:582] - Received message: root=InitializedNotification(method='notifications/initialized', params=None, jsonrpc='2.0')
2026-01-28 10:02:40 - mcp.server.lowlevel.server - DEBUG - [server.py:582] - Received message: <mcp.shared.session.RequestResponder object at 0x000001407A58E3F0>
2026-01-28 10:02:40 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type ListToolsRequest
2026-01-28 10:02:40 - mcp.server.lowlevel.server - DEBUG - [server.py:621] - Dispatching request of type ListToolsRequest
2026-01-28 10:02:40 - lzwcai_mcpskills_template.main - INFO - [main.py:143] - 收到 ListTools 请求,当前配置数量: 8
2026-01-28 10:02:40 - lzwcai_mcpskills_template.main - INFO - [main.py:162] - ListTools 响应: 返回 8 个工具
2026-01-28 10:02:40 - mcp.server.lowlevel.server - DEBUG - [server.py:662] - Response sent
2026-01-28 10:02:44 - mcp.server.lowlevel.server - DEBUG - [server.py:582] - Received message: <mcp.shared.session.RequestResponder object at 0x000001407A66FAD0>
2026-01-28 10:02:44 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest
2026-01-28 10:02:44 - mcp.server.lowlevel.server - DEBUG - [server.py:621] - Dispatching request of type CallToolRequest
2026-01-28 10:02:44 - lzwcai_mcpskills_template.main - INFO - [main.py:172] - 收到 CallTool 请求: name=demo_complex, arguments={"user_info": {}}
2026-01-28 10:02:44 - lzwcai_mcpskills_template.main - INFO - [main.py:189] - 工具执行成功: demo_complex
2026-01-28 10:02:44 - mcp.server.lowlevel.server - DEBUG - [server.py:662] - Response sent
2026-01-28 10:02:50 - mcp.server.lowlevel.server - DEBUG - [server.py:582] - Received message: <mcp.shared.session.RequestResponder object at 0x000001407A1DAF00>
2026-01-28 10:02:50 - mcp.server.lowlevel.server - INFO - [server.py:619] - Processing request of type CallToolRequest
2026-01-28 10:02:50 - mcp.server.lowlevel.server - DEBUG - [server.py:621] - Dispatching request of type CallToolRequest
2026-01-28 10:02:50 - lzwcai_mcpskills_template.main - INFO - [main.py:172] - 收到 CallTool 请求: name=demo_mixed, arguments=None
2026-01-28 10:02:50 - lzwcai_mcpskills_template.main - INFO - [main.py:189] - 工具执行成功: demo_mixed
2026-01-28 10:02:50 - mcp.server.lowlevel.server - DEBUG - [server.py:662] - Response sent

View File

@@ -0,0 +1,8 @@
2026-01-28 09:58:55 - lzwcai_mcpskills_template.main - ERROR - [main.py:190] - 工具执行失败: 这是一个演示用的模拟错误!执行被中断。
Traceback (most recent call last):
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcpskills_template\lzwcai_mcpskills_template\main.py", line 186, in handle_call_tool
result_contents = execute_tool(name, arguments or {}, tool_config)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_mcpskills_template\lzwcai_mcpskills_template\main.py", line 259, in execute_tool
raise ValueError("这是一个演示用的模拟错误!执行被中断。")
ValueError: 这是一个演示用的模拟错误!执行被中断。

View File

@@ -0,0 +1,345 @@
#!/usr/bin/env python3
"""
MCP Server 模板
支持从本地 JSON 或 API 动态加载工具配置
使用方式:
- local: 从本地 JSON 文件加载工具配置
- api: 从远程 API 加载工具配置
"""
import json
import os
import logging
import argparse
import anyio
import asyncio
import mcp.types as types
from mcp.server import NotificationOptions, Server
from mcp.server.models import InitializationOptions
from mcp.server.stdio import stdio_server
try:
from .schema_converter import convert_sql_params_to_input_schema
from .utils.api_client import call_api
from .utils.env_config import get_api_key, get_base_url
from .utils.logger_config import setup_system_logging, get_logger
except ImportError:
from schema_converter import convert_sql_params_to_input_schema
from utils.api_client import call_api
from utils.env_config import get_api_key, get_base_url
from utils.logger_config import setup_system_logging, get_logger
# 初始化日志系统
setup_system_logging(app_name="lzwcai_mcpskills_template", log_level=logging.DEBUG)
logger = get_logger(__name__)
# 初始化 MCP Server
server = Server("mcpskills_template_server")
# 全局配置
_tools_config: list[dict] = []
_config: dict = {}
def parse_arguments():
"""解析命令行参数"""
parser = argparse.ArgumentParser(description="MCP Skills Template Server")
parser.add_argument(
"--mode",
type=str,
choices=["local", "api"],
default="local",
help="数据加载模式: local(本地JSON默认) 或 api(远程API)"
)
parser.add_argument(
"--json-path",
type=str,
default=None,
help="本地 JSON 文件路径 (local 模式)"
)
return parser.parse_args()
class DataLoader:
"""数据加载器基类"""
def load(self) -> list[dict]:
raise NotImplementedError
class LocalLoader(DataLoader):
"""本地 JSON 文件加载器"""
def __init__(self, json_path: str = None):
if json_path is None:
json_path = os.path.join(os.path.dirname(__file__), "tools_config.json")
self.json_path = json_path
def load(self) -> list[dict]:
try:
with open(self.json_path, "r", encoding="utf-8") as f:
data = json.load(f)
logger.info(f"从本地加载 {len(data)} 条配置: {self.json_path}")
return data
except FileNotFoundError:
logger.error(f"配置文件不存在: {self.json_path}")
return []
except json.JSONDecodeError as e:
logger.error(f"JSON 解析错误: {e}")
return []
class ApiLoader(DataLoader):
"""API 远程加载器"""
def __init__(self):
self.base_url = get_base_url()
self.api_key = get_api_key()
logger.debug(f"ApiLoader 初始化base_url: {self.base_url}")
def load(self) -> list[dict]:
try:
# TODO: 根据实际 API 修改此处
logger.info(f"开始从 API 加载工具配置")
response = call_api("/api/tools", method="GET")
if response.get("code") != 200:
logger.error(f"获取工具配置失败: {response.get('msg')}")
return []
data = response.get("data", [])
if isinstance(data, dict):
data = [data]
logger.info(f"从 API 加载工具配置成功,数量: {len(data)}")
return data
except Exception as e:
logger.error(f"API 请求失败: {e}", exc_info=True)
return []
def create_loader(mode: str, **kwargs) -> DataLoader:
"""创建数据加载器"""
if mode == "local":
return LocalLoader(json_path=kwargs.get("json_path"))
elif mode == "api":
return ApiLoader()
else:
raise ValueError(f"不支持的模式: {mode}")
def init_tools(loader: DataLoader):
"""初始化工具配置"""
global _tools_config
_tools_config = loader.load()
logger.info(f"已加载 {len(_tools_config)} 个工具配置")
@server.list_tools()
async def handle_list_tools() -> list[types.Tool]:
"""列出所有可用工具"""
logger.info(f"收到 ListTools 请求,当前配置数量: {len(_tools_config)}")
tools = []
for tool in _tools_config:
name = tool.get("name", "")
description = tool.get("description") or tool.get("toolPrompt", "")
sql_params = tool.get("sqlParams", "[]")
# 转换参数为 inputSchema
input_schema = convert_sql_params_to_input_schema(sql_params)
tools.append(
types.Tool(
name=name,
description=description,
inputSchema=input_schema
)
)
logger.info(f"ListTools 响应: 返回 {len(tools)} 个工具")
return tools
@server.call_tool()
async def handle_call_tool(
name: str,
arguments: dict | None
) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
"""调用工具"""
logger.info(f"收到 CallTool 请求: name={name}, arguments={json.dumps(arguments, ensure_ascii=False) if arguments else 'None'}")
# 查找对应的工具配置
tool_config = None
for tool in _tools_config:
if tool.get("name") == name:
tool_config = tool
break
if tool_config is None:
logger.error(f"未找到工具配置: {name}")
raise ValueError(f"未知工具: {name}")
# TODO: 在这里实现你的工具逻辑
try:
# execute_tool 现在是 async 的
result_contents = await execute_tool(name, arguments or {}, tool_config)
logger.info(f"工具执行成功: {name}")
return result_contents
except Exception as e:
logger.error(f"工具执行失败: {e}", exc_info=True)
# 重新抛出异常,以便 MCP SDK 能够捕获并将其格式化为错误响应
raise e
async def execute_tool(name: str, arguments: dict, config: dict) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
"""
执行工具逻辑并返回 MCP 内容列表 (Async)
"""
# 1. 示例Hello World (返回纯文本)
if name == "example_hello_world":
msg = f"Hello, {arguments.get('name', 'World')}!"
return [types.TextContent(type="text", text=msg)]
# 2. 示例:计算器 (返回 JSON 格式的文本)
elif name == "example_calculator":
a = float(arguments.get("a", 0))
b = float(arguments.get("b", 0))
op = arguments.get("operation", "+")
if op == "+":
result = a + b
elif op == "-":
result = a - b
elif op == "*":
result = a * b
elif op == "/":
result = a / b if b != 0 else "除数不能为0"
else:
result = "未知运算符"
output = {"result": result, "expression": f"{a} {op} {b}"}
return [types.TextContent(type="text", text=json.dumps(output, ensure_ascii=False))]
# 3. 演示:纯文本
elif name == "demo_text":
return [types.TextContent(type="text", text="这是一个标准的纯文本输出示例。")]
# 4. 演示:图片输出
elif name == "demo_image":
# 这是一个 1x1 的红色像素点的 Base64
red_dot_base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=="
return [
types.ImageContent(
type="image",
data=red_dot_base64,
mimeType="image/png"
)
]
# 5. 演示:资源输出
elif name == "demo_resource":
# 模拟返回一个嵌入式资源
return [
types.EmbeddedResource(
type="resource",
resource=types.TextResourceContents(
uri="file:///logs/app.log",
text="[INFO] System started\n[WARN] Low memory",
mimeType="text/plain"
)
)
]
# 6. 演示:错误状态 (带 observation)
elif name == "demo_error":
# 尝试模拟用户要求的带 observation 的错误结构
# 注意:标准 MCP 协议中 TextContent 可能不支持 observation 字段
# 这里演示如何抛出异常,这是最标准的错误反馈方式
raise ValueError("执行失败: 网络超时,请检查连接")
# 如果客户端支持非标准字段,可以尝试返回如下结构(需 SDK 支持):
# return [
# types.TextContent(type="text", text="执行失败") # 无法直接添加 observation
# ]
# 7. 演示:混合输出 (文本 + 图片)
elif name == "demo_mixed":
red_dot_base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=="
return [
types.TextContent(type="text", text="下面是一张图片:"),
types.ImageContent(
type="image",
data=red_dot_base64,
mimeType="image/png"
),
types.TextContent(type="text", text="图片展示完毕。")
]
# 8. 演示:复杂参数
elif name == "demo_complex":
user_info = arguments.get("user_info", {})
tags = arguments.get("tags", [])
# 模拟异步处理
await asyncio.sleep(0.1)
result_text = f"接收到复杂参数:\n用户: {json.dumps(user_info, ensure_ascii=False)}\n标签: {tags}"
return [types.TextContent(type="text", text=result_text)]
# 默认返回参数
default_result = {
"tool_name": name,
"arguments": arguments,
"message": "工具执行成功(默认实现)"
}
return [types.TextContent(type="text", text=json.dumps(default_result, ensure_ascii=False, indent=2))]
async def run_server():
"""运行 MCP Server (stdio 模式)"""
async with stdio_server() as streams:
await server.run(
streams[0],
streams[1],
InitializationOptions(
server_name="mcpskills_template_server",
server_version="0.1.0",
capabilities=server.get_capabilities(
notification_options=NotificationOptions(),
experimental_capabilities={},
),
),
)
def main():
"""主入口"""
global _config
logger.info("=" * 50)
logger.info("MCP Skills Template Server 启动")
logger.info("=" * 50)
# 解析命令行参数
args = parse_arguments()
_config = vars(args)
logger.info(f"命令行参数: {_config}")
# 创建加载器并初始化工具
logger.info(f"使用模式: {args.mode}")
loader = create_loader(
mode=args.mode,
json_path=args.json_path
)
init_tools(loader)
logger.info("开始运行 MCP Server (stdio 模式)")
# 运行服务器
anyio.run(run_server)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,185 @@
"""
Schema 转换器
将 sqlParams 数组格式转换为 MCP 工具需要的 JSON Schema 格式
支持的类型:
- string: 文本输入
- paragraph: 段落/多行文本
- select: 下拉选项
- number: 数字输入
"""
import json
from typing import Any
def convert_param_to_schema_property(param: dict) -> tuple[str, dict, bool]:
"""
将单个参数转换为 JSON Schema property
Args:
param: 参数配置,格式如:
{
"type": "string",
"name": "param_name",
"displayName": "参数显示名",
"maxLength": 200,
"defaultValue": "",
"required": true,
"options": ["选项1", "选项2"] # 仅 select 类型
}
Returns:
tuple: (property_name, property_schema, is_required)
"""
param_type = param.get("type", "string")
param_name = param.get("name", "")
display_name = param.get("displayName", param_name)
default_value = param.get("defaultValue", "")
max_length = param.get("maxLength")
is_required = param.get("required", False)
options = param.get("options", [])
property_schema = {
"description": display_name
}
if param_type == "string":
property_schema["type"] = "string"
if max_length:
property_schema["maxLength"] = max_length
elif param_type == "paragraph":
property_schema["type"] = "string"
property_schema["format"] = "paragraph"
if max_length:
property_schema["maxLength"] = max_length
elif param_type == "select":
property_schema["type"] = "string"
if options:
property_schema["enum"] = options
elif param_type == "number":
property_schema["type"] = "number"
elif param_type == "boolean":
property_schema["type"] = "boolean"
# Boolean default handling
if default_value is not None and str(default_value).lower() in ("true", "false", "1", "0"):
property_schema["default"] = str(default_value).lower() in ("true", "1")
elif param_type == "array":
property_schema["type"] = "array"
# Simple array of strings by default if no item type specified
# For more complex arrays, we might need extended config in sqlParams
property_schema["items"] = {"type": "string"}
elif param_type == "object":
property_schema["type"] = "object"
# Allow any object structure by default
property_schema["additionalProperties"] = True
else:
# 默认当作 string 处理
property_schema["type"] = "string"
# 添加默认值
if default_value not in (None, ""):
if param_type == "number":
try:
property_schema["default"] = int(default_value) if str(default_value).isdigit() else float(default_value)
except (ValueError, TypeError):
property_schema["default"] = default_value
else:
property_schema["default"] = default_value
return param_name, property_schema, is_required
def convert_sql_params_to_input_schema(sql_params: str | list) -> dict:
"""
将 sqlParams 转换为 MCP 工具的 inputSchema
Args:
sql_params: sqlParams 字段值,可以是 JSON 字符串或已解析的列表
格式: [{"type": "string", "name": "xxx", ...}, ...]
Returns:
dict: MCP 工具的 inputSchema格式如:
{
"type": "object",
"properties": {...},
"required": [...]
}
"""
# 解析 JSON 字符串
if isinstance(sql_params, str):
try:
params_list = json.loads(sql_params)
except json.JSONDecodeError:
return {"type": "object", "properties": {}, "required": []}
else:
params_list = sql_params
if not isinstance(params_list, list):
return {"type": "object", "properties": {}, "required": []}
input_schema = {
"type": "object",
"properties": {},
"required": []
}
for param in params_list:
if not isinstance(param, dict):
continue
name, schema, is_required = convert_param_to_schema_property(param)
if name:
input_schema["properties"][name] = schema
if is_required:
input_schema["required"].append(name)
return input_schema
def convert_tool_config_to_mcp_tool(config: dict) -> dict:
"""
将单个工具配置转换为 MCP Tool 配置
Args:
config: 工具配置对象
Returns:
dict: MCP Tool 配置
"""
name = config.get("name", "")
description = config.get("description") or config.get("toolPrompt", "")
sql_params = config.get("sqlParams", "[]")
input_schema = convert_sql_params_to_input_schema(sql_params)
return {
"name": name,
"description": description,
"inputSchema": input_schema,
"_raw": config # 保留原始数据
}
# 测试用
if __name__ == "__main__":
# 测试单个参数转换
test_param = {
"type": "select",
"name": "operation",
"displayName": "运算符",
"required": True,
"options": ["+", "-", "*", "/"]
}
name, schema, required = convert_param_to_schema_property(test_param)
print(f"参数名: {name}")
print(f"Schema: {json.dumps(schema, ensure_ascii=False, indent=2)}")
print(f"必填: {required}")

View File

@@ -0,0 +1,58 @@
[
{
"id": "example_tool_001",
"name": "example_hello_world2",
"description": "示例工具 - Hello World2",
"toolPrompt": "这是一个示例工具,用于演示 MCP 工具的基本结构",
"sqlParams": "[{\"type\":\"string\",\"name\":\"name2\",\"displayName\":\"名称\",\"maxLength\":100,\"defaultValue\":\"World\",\"required\":true}]"
},
{
"id": "example_tool_002",
"name": "example_calculator",
"description": "示例工具 - 简单计算器",
"toolPrompt": "执行简单的数学计算",
"sqlParams": "[{\"type\":\"number\",\"name\":\"a\",\"displayName\":\"数字A\",\"required\":true},{\"type\":\"number\",\"name\":\"b\",\"displayName\":\"数字B\",\"required\":true},{\"type\":\"select\",\"name\":\"operation\",\"displayName\":\"运算符\",\"required\":true,\"options\":[\"+\",\"-\",\"*\",\"/\"]}]"
},
{
"id": "demo_001",
"name": "demo_text",
"description": "演示纯文本输出",
"toolPrompt": "返回一段简单的文本信息",
"sqlParams": "[]"
},
{
"id": "demo_002",
"name": "demo_image",
"description": "演示图片输出",
"toolPrompt": "返回一张 Base64 编码的图片",
"sqlParams": "[]"
},
{
"id": "demo_003",
"name": "demo_resource",
"description": "演示资源输出",
"toolPrompt": "返回一个嵌入式资源",
"sqlParams": "[]"
},
{
"id": "demo_004",
"name": "demo_error",
"description": "演示错误状态",
"toolPrompt": "触发一个执行错误",
"sqlParams": "[]"
},
{
"id": "demo_005",
"name": "demo_mixed",
"description": "演示混合输出",
"toolPrompt": "同时返回文本和图片",
"sqlParams": "[]"
},
{
"id": "demo_006",
"name": "demo_complex",
"description": "演示复杂参数输入",
"toolPrompt": "接受对象和数组类型的参数",
"sqlParams": "[{\"type\":\"object\",\"name\":\"user_info\",\"displayName\":\"用户信息\",\"required\":true},{\"type\":\"array\",\"name\":\"tags\",\"displayName\":\"标签列表\",\"required\":false}]"
}
]

View File

@@ -0,0 +1,19 @@
"""Utils package for lzwcai_mcpskills_template"""
from .json_helper import load_json
from .api_client import call_api, APIClient, get_default_client
from .env_config import get_api_key, get_base_url, get_env_config, set_env_variable
from .logger_config import setup_system_logging, get_logger
__all__ = [
'load_json',
'call_api',
'APIClient',
'get_default_client',
'get_api_key',
'get_base_url',
'get_env_config',
'set_env_variable',
'setup_system_logging',
'get_logger'
]

View File

@@ -0,0 +1,197 @@
"""
API 调用客户端
通用的 HTTP API 客户端封装
"""
import httpx
import json
from typing import Dict, Any, Optional
try:
from .env_config import get_base_url, get_api_key
from .logger_config import get_logger
except ImportError:
from env_config import get_base_url, get_api_key
from logger_config import get_logger
logger = get_logger(__name__)
# 默认超时配置(秒)
DEFAULT_TIMEOUT = 30.0
LONG_TIMEOUT = 300.0
class APIClient:
"""通用 API 客户端"""
def __init__(
self,
base_url: Optional[str] = None,
api_key: Optional[str] = None,
default_timeout: float = DEFAULT_TIMEOUT
):
"""
初始化 API 客户端
Args:
base_url: API 基础 URL默认从环境变量读取
api_key: API 密钥(默认从环境变量读取)
default_timeout: 默认超时时间(秒)
"""
self.base_url = (base_url or get_base_url()).rstrip('/')
self.api_key = api_key or get_api_key()
self.default_timeout = default_timeout
self._client: Optional[httpx.Client] = None
logger.info(f"[客户端初始化] base_url={self.base_url}")
@property
def client(self) -> httpx.Client:
"""懒加载 HTTP 客户端"""
if self._client is None:
self._client = httpx.Client(timeout=self.default_timeout)
return self._client
def _get_headers(self) -> Dict[str, str]:
"""获取请求头"""
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
if self.api_key:
headers['X-API-Key'] = self.api_key
return headers
def request(
self,
endpoint: str,
method: str = "GET",
data: Optional[Dict[str, Any]] = None,
params: Optional[Dict[str, Any]] = None,
timeout: Optional[float] = None
) -> Dict[str, Any]:
"""
发送 HTTP 请求
Args:
endpoint: API 端点路径
method: HTTP 方法
data: 请求体数据
params: URL 查询参数
timeout: 超时时间
Returns:
API 响应数据
"""
url = f"{self.base_url}{endpoint}"
timeout = timeout or self.default_timeout
try:
logger.info(f"[API请求] {method} {url}")
if method.upper() == "GET":
response = self.client.get(
url,
headers=self._get_headers(),
params=params,
timeout=timeout
)
elif method.upper() == "POST":
response = self.client.post(
url,
headers=self._get_headers(),
json=data,
params=params,
timeout=timeout
)
elif method.upper() == "PUT":
response = self.client.put(
url,
headers=self._get_headers(),
json=data,
params=params,
timeout=timeout
)
elif method.upper() == "DELETE":
response = self.client.delete(
url,
headers=self._get_headers(),
params=params,
timeout=timeout
)
else:
raise ValueError(f"不支持的 HTTP 方法: {method}")
logger.info(f"[API响应] HTTP {response.status_code}")
response.raise_for_status()
return response.json()
except httpx.TimeoutException:
error_msg = f"API 请求超时: {url}"
logger.error(f"[API错误] {error_msg}")
raise Exception(error_msg)
except httpx.HTTPStatusError as e:
error_msg = f"API 请求失败 (HTTP {e.response.status_code}): {url}"
logger.error(f"[API错误] {error_msg}")
raise Exception(error_msg)
except Exception as e:
error_msg = f"API 请求异常: {str(e)}"
logger.error(f"[API错误] {error_msg}", exc_info=True)
raise Exception(error_msg)
def close(self):
"""关闭 HTTP 客户端"""
if self._client is not None:
self._client.close()
self._client = None
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
return False
# 懒加载的默认客户端
_default_client: Optional[APIClient] = None
def get_default_client() -> APIClient:
"""获取默认客户端(懒加载)"""
global _default_client
if _default_client is None:
_default_client = APIClient()
return _default_client
def call_api(
endpoint: str,
method: str = "GET",
data: Optional[Dict[str, Any]] = None,
params: Optional[Dict[str, Any]] = None,
timeout: Optional[float] = None
) -> Dict[str, Any]:
"""
便捷函数:调用 API
Args:
endpoint: API 端点路径
method: HTTP 方法
data: 请求体数据
params: URL 查询参数
timeout: 超时时间
Returns:
API 响应数据
"""
return get_default_client().request(
endpoint=endpoint,
method=method,
data=data,
params=params,
timeout=timeout
)

View File

@@ -0,0 +1,60 @@
"""环境变量配置模块"""
import os
from typing import Optional
def get_api_key(default: str = "") -> str:
"""
获取 API 密钥环境变量
Args:
default: 默认值
Returns:
str: API 密钥
Environment Variables:
API_KEY: API 密钥
"""
return os.environ.get("API_KEY", default)
def get_base_url(default: str = "http://localhost:8080") -> str:
"""
获取后端 API 基础 URL 环境变量
Args:
default: 默认值
Returns:
str: 后端 API 基础 URL
Environment Variables:
BASE_URL: 后端 API 基础 URL
"""
return os.environ.get("BASE_URL", default)
def get_env_config() -> dict:
"""
获取所有环境配置
Returns:
dict: 包含所有配置的字典
"""
return {
"api_key": get_api_key(),
"base_url": get_base_url()
}
def set_env_variable(key: str, value: str) -> None:
"""
设置环境变量(仅在当前进程中有效)
Args:
key: 环境变量名
value: 环境变量值
"""
os.environ[key] = value

View File

@@ -0,0 +1,41 @@
"""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 文件的路径
Returns:
JSON 文件中解析后的数据
Raises:
FileNotFoundError: 当文件不存在时
json.JSONDecodeError: 当 JSON 格式无效时
"""
try:
path = Path(json_path)
if not path.exists():
raise FileNotFoundError(f"JSON 文件不存在: {json_path}")
if not path.is_file():
raise ValueError(f"路径不是一个文件: {json_path}")
with open(path, 'r', encoding='utf-8') as f:
data = json.load(f)
return data
except json.JSONDecodeError as e:
raise json.JSONDecodeError(f"JSON 格式错误: {e.msg}", e.doc, e.pos)
except FileNotFoundError:
raise
except Exception as e:
raise Exception(f"读取 JSON 文件时发生错误: {str(e)}")

View File

@@ -0,0 +1,130 @@
# -*- coding: utf-8 -*-
"""
统一日志配置模块
提供系统级别的日志配置和管理
"""
import os
import sys
import logging
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler
from pathlib import Path
class LoggerConfig:
"""日志配置管理类"""
def __init__(self, logs_dir: str = None):
"""初始化日志配置"""
if logs_dir:
self.logs_dir = Path(logs_dir)
else:
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'
self.log_level = self._get_log_level_from_env()
self._initialized = False
def _get_log_level_from_env(self) -> 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_mcpskills_template",
log_level: int = logging.INFO,
max_file_size: int = 10 * 1024 * 1024,
backup_count: int = 5,
console_output: bool = True) -> 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_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,
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)
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}")
return root_logger
def get_module_logger(self, module_name: str) -> logging.Logger:
return logging.getLogger(module_name)
# 全局日志配置实例
logger_config = LoggerConfig()
def setup_system_logging(app_name: str = "lzwcai_mcpskills_template",
log_level: int = logging.INFO) -> logging.Logger:
return logger_config.setup_logging(app_name, log_level)
def get_logger(name: str) -> logging.Logger:
return logger_config.get_module_logger(name)

View File

@@ -0,0 +1,15 @@
"""
Entry point for lzwcai-mcpskills-template
MCP Server 模板项目入口
"""
import os
if __name__ == "__main__":
# 设置环境变量(根据实际需求修改)
os.environ["API_KEY"] = "your-api-key"
os.environ["BASE_URL"] = "http://localhost:8080"
# Import and run the actual MCP server
from lzwcai_mcpskills_template.main import main
main()

View File

@@ -0,0 +1,31 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "lzwcai_mcpskills_template"
version = "0.1.3"
description = "MCP Server 模板项目 - 用于快速创建新的 MCP 服务"
readme = "README.md"
requires-python = ">=3.10"
license = {text = "MIT"}
authors = [
{name = "lzwcai", email = "your-email@example.com"},
]
keywords = ["mcp", "template", "server"]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
]
dependencies = [
"httpx>=0.28.1",
"mcp[cli]>=1.10.1",
]
[project.scripts]
lzwcai_mcpskills_template = "lzwcai_mcpskills_template.main:main"
[tool.hatch.build.targets.wheel]
packages = ["lzwcai_mcpskills_template"]

View File

@@ -0,0 +1,37 @@
# lzwcai_mcpskills_visual2url
基于 FastMCP 的 MCP Server 项目。主要提供 HTML 转换为浏览器可访问 URL 的工具。
## 功能
提供以下两个工具Tools
- `html_file_to_url`: 传入本地 HTML 文件路径,转换成 `file://` 协议的 URL方便浏览器直接打开预览。
- `html_code_to_url`: 传入 HTML 代码片段,将其保存为临时文件并返回可访问的 `file://` URL。
## 安装
```bash
pip install lzwcai_mcpskills_visual2url
```
## 使用
```bash
# 直接运行服务端
lzwcai_mcpskills_visual2url
```
## 项目结构
```
lzwcai_mcpskills_visual2url/
├── main.py # 入口文件
├── pyproject.toml # 项目配置
├── README.md # 说明文档
└── lzwcai_mcpskills_visual2url/ # 核心代码
└── main.py # FastMCP Server 主逻辑
```
## License
MIT

View File

@@ -0,0 +1,72 @@
from mcp.server.fastmcp import FastMCP
import os
import requests
# 创建 FastMCP 实例
mcp = FastMCP("Visual2URL")
UPLOAD_URL = "http://192.168.2.236:5002/api/html/upload"
AUTHORIZATION = "Bearer eyJhbGciOiJIUzUxMiJ9.eyJ0b2tlbl90eXBlIjoiTE9HSU4iLCJsb2dpbl91c2VyX2tleSI6IjM3OTAzNGIwLTg3OTQtNGU2OS05MzRiLTJkNTA3ZWIzODQyOCJ9.X5O0zez0_zm4Ejc9wTyj04Sf3GLT2L9ilIE6W0qbSgggdTvKcaboVm-A64U3SPxC8rpB1Gxc5p-XK7FXqAjgEg"
@mcp.tool()
def html_file_to_url(file_path: str) -> str:
"""
将本地 HTML 文件上传至服务器并转换成可在浏览器中打开的 URL。
Args:
file_path: 本地 HTML 文件的绝对路径
"""
if not os.path.exists(file_path):
return f"Error: 文件不存在: {file_path}"
headers = {
'Authorization': AUTHORIZATION,
'User-Agent': 'Apifox/1.0.0 (https://apifox.com)'
}
try:
with open(file_path, 'rb') as f:
files = {
'file': (os.path.basename(file_path), f, 'text/html')
}
response = requests.post(UPLOAD_URL, headers=headers, files=files)
response.raise_for_status()
data = response.json()
if data.get('code') == 200 and 'data' in data and 'fileUrl' in data['data']:
return data['data']['fileUrl']
else:
return f"上传失败: {data.get('msg', '未知错误')}"
except Exception as e:
return f"请求发生异常: {str(e)}"
@mcp.tool()
def html_code_to_url(html_code: str) -> str:
"""
将 HTML 代码片段上传至服务器并转换成可在浏览器中打开的 URL。
Args:
html_code: HTML 代码字符串
"""
headers = {
'Authorization': AUTHORIZATION,
'User-Agent': 'Apifox/1.0.0 (https://apifox.com)',
'Content-Type': 'text/html'
}
try:
response = requests.post(UPLOAD_URL, headers=headers, data=html_code.encode('utf-8'))
response.raise_for_status()
data = response.json()
if data.get('code') == 200 and 'data' in data and 'fileUrl' in data['data']:
return data['data']['fileUrl']
else:
return f"上传失败: {data.get('msg', '未知错误')}"
except Exception as e:
return f"请求发生异常: {str(e)}"
def main():
# 运行 MCP Server
mcp.run()
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,9 @@
"""
Entry point for lzwcai_mcpskills_visual2url
MCP Server 项目入口
"""
if __name__ == "__main__":
# Import and run the actual MCP server
from lzwcai_mcpskills_visual2url.main import main
main()

View File

@@ -0,0 +1,31 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "lzwcai_mcpskills_visual2url"
version = "0.1.1"
description = "MCP Server 模板项目 - 用于快速创建新的 MCP 服务"
readme = "README.md"
requires-python = ">=3.10"
license = {text = "MIT"}
authors = [
{name = "lzwcai", email = "your-email@example.com"},
]
keywords = ["mcp", "template", "server"]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
]
dependencies = [
"mcp>=1.0.0",
"requests>=2.31.0",
]
[project.scripts]
lzwcai_mcpskills_visual2url = "lzwcai_mcpskills_visual2url.main:main"
[tool.hatch.build.targets.wheel]
packages = ["lzwcai_mcpskills_visual2url"]

View File

@@ -0,0 +1,137 @@
2026-03-28 12:30:33 - root - INFO - [logger_config.py:117] - 日志系统初始化完成 - 日志目录: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_workflow_to_mcp\lzwcai_workflow_to_mcp\logs
2026-03-28 12:30:33 - mcp.server.lowlevel.server - DEBUG - [server.py:162] - Initializing server 'workflow_mcp_server'
2026-03-28 12:30:33 - mcp.server.lowlevel.server - DEBUG - [server.py:439] - Registering handler for ListToolsRequest
2026-03-28 12:30:33 - mcp.server.lowlevel.server - DEBUG - [server.py:519] - Registering handler for CallToolRequest
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.main - INFO - [main.py:334] - ==================================================
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.main - INFO - [main.py:335] - Workflow MCP Server 启动
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.main - INFO - [main.py:336] - ==================================================
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.main - INFO - [main.py:341] - 命令行参数: {'mode': 'api', 'json_path': None, 'workflow_id': None}
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.main - INFO - [main.py:344] - 使用模式: api
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.main - DEBUG - [main.py:103] - ApiLoader 初始化工作流ID: 2037527155235225601
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.main - INFO - [main.py:111] - 开始从 API 加载工作流配置工作流ID: 2037527155235225601
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.utils.api_client - INFO - [api_client.py:115] - [客户端初始化] base_url=http://192.168.2.236:8088
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.utils.api_client - INFO - [api_client.py:116] - [客户端初始化] token=wf_bd39a583670c42ceab48b3353bf2ba43
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.utils.api_client - INFO - [api_client.py:117] - [客户端初始化] execute_timeout=600.0s
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.utils.api_client - INFO - [api_client.py:148] - [API请求] GET http://192.168.2.236:8088/system/workflowManage/getByWorkflowId/2037527155235225601
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.utils.api_client - DEBUG - [api_client.py:149] - [API请求] Headers: {'X-API-Key': 'wf_bd39a583670c42ceab48b3353bf2ba43'}
2026-03-28 12:30:34 - httpcore.connection - DEBUG - [_trace.py:47] - connect_tcp.started host='192.168.2.236' port=8088 local_address=None timeout=30.0 socket_options=None
2026-03-28 12:30:34 - httpcore.connection - DEBUG - [_trace.py:47] - connect_tcp.complete return_value=<httpcore._backends.sync.SyncStream object at 0x0000016702CF9970>
2026-03-28 12:30:34 - httpcore.http11 - DEBUG - [_trace.py:47] - send_request_headers.started request=<Request [b'GET']>
2026-03-28 12:30:34 - httpcore.http11 - DEBUG - [_trace.py:47] - send_request_headers.complete
2026-03-28 12:30:34 - httpcore.http11 - DEBUG - [_trace.py:47] - send_request_body.started request=<Request [b'GET']>
2026-03-28 12:30:34 - httpcore.http11 - DEBUG - [_trace.py:47] - send_request_body.complete
2026-03-28 12:30:34 - httpcore.http11 - DEBUG - [_trace.py:47] - receive_response_headers.started request=<Request [b'GET']>
2026-03-28 12:30:34 - httpcore.http11 - DEBUG - [_trace.py:47] - receive_response_headers.complete return_value=(b'HTTP/1.1', 200, b'', [(b'Vary', b'Origin'), (b'Vary', b'Access-Control-Request-Method'), (b'Vary', b'Access-Control-Request-Headers'), (b'X-Content-Type-Options', b'nosniff'), (b'X-XSS-Protection', b'1; mode=block'), (b'X-Frame-Options', b'SAMEORIGIN'), (b'Content-Type', b'application/json'), (b'Transfer-Encoding', b'chunked'), (b'Date', b'Sat, 28 Mar 2026 04:30:30 GMT'), (b'Keep-Alive', b'timeout=60'), (b'Connection', b'keep-alive')])
2026-03-28 12:30:34 - httpx - INFO - [_client.py:1025] - HTTP Request: GET http://192.168.2.236:8088/system/workflowManage/getByWorkflowId/2037527155235225601 "HTTP/1.1 200 "
2026-03-28 12:30:34 - httpcore.http11 - DEBUG - [_trace.py:47] - receive_response_body.started request=<Request [b'GET']>
2026-03-28 12:30:34 - httpcore.http11 - DEBUG - [_trace.py:47] - receive_response_body.complete
2026-03-28 12:30:34 - httpcore.http11 - DEBUG - [_trace.py:47] - response_closed.started
2026-03-28 12:30:34 - httpcore.http11 - DEBUG - [_trace.py:47] - response_closed.complete
2026-03-28 12:30:34 - lzwcai_workflow_to_mcp.utils.api_client - INFO - [api_client.py:156] - [API响应] HTTP 200
2026-03-28 12:30:34 - lzwcai_workflow_to_mcp.utils.api_client - DEBUG - [api_client.py:157] - [API响应] Headers: {'vary': 'Origin, Access-Control-Request-Method, Access-Control-Request-Headers', 'x-content-type-options': 'nosniff', 'x-xss-protection': '1; mode=block', 'x-frame-options': 'SAMEORIGIN', 'content-type': 'application/json', 'transfer-encoding': 'chunked', 'date': 'Sat, 28 Mar 2026 04:30:30 GMT', 'keep-alive': 'timeout=60', 'connection': 'keep-alive'}
2026-03-28 12:30:34 - lzwcai_workflow_to_mcp.utils.api_client - INFO - [api_client.py:162] - [API响应] 获取工作流配置成功: workflow_id=2037527155235225601
2026-03-28 12:30:34 - lzwcai_workflow_to_mcp.utils.api_client - DEBUG - [api_client.py:163] - [API响应] Body: {
"msg": "查询成功",
"code": 200,
"data": [
{
"id": "2037535460665982977",
"createBy": "duchangyuan",
"createTime": "2026-03-27 22:21:24",
"updateBy": "admin",
"updateTime": "2026-03-28 12:21:49",
"serviceId": "2037535460632428546",
"uniqueName": "银行流水状态检测",
"name": "yinxingliushuizhuangtaijiance_7c4d9a63",
"description": "用于检测流水导入状态,检查是否全部导入完成",
"visualizable": 0,
"toolPrompt": null,
"toolType": "api",
"datasourceId": null,
"sqlTemplate": null,
"sqlParams": "[]",
"resultType": null,
"sourceType": null,
"trainingTaskId": null,
"tableMetadataIds": null,
"executionCount": 0,
"visualizationConfigs": null,
"inputJsonSchema": "{\"type\":\"object\",\"properties\":{\"workflow_extraContext\":{\"description\":\"工作流额外的上下文参数(如环境变量等),可以是任何类型,非必填\"}},\"required\":[]}",
"outputJsonSchema": "{\"type\":\"object\",\"properties\":{\"text\":{\"type\":\"string\"}},\"additionalProperties\":false}",
"lastExecutionTime": null
}
]
}
2026-03-28 12:30:34 - lzwcai_workflow_to_mcp.main - DEBUG - [main.py:116] - API 响应原始数据: {
"msg": "查询成功",
"code": 200,
"data": [
{
"id": "2037535460665982977",
"createBy": "duchangyuan",
"createTime": "2026-03-27 22:21:24",
"updateBy": "admin",
"updateTime": "2026-03-28 12:21:49",
"serviceId": "2037535460632428546",
"uniqueName": "银行流水状态检测",
"name": "yinxingliushuizhuangtaijiance_7c4d9a63",
"description": "用于检测流水导入状态,检查是否全部导入完成",
"visualizable": 0,
"toolPrompt": null,
"toolType": "api",
"datasourceId": null,
"sqlTemplate": null,
"sqlParams": "[]",
"resultType": null,
"sourceType": null,
"trainingTaskId": null,
"tableMetadataIds": null,
"executionCount": 0,
"visualizationConfigs": null,
"inputJsonSchema": "{\"type\":\"object\",\"properties\":{\"workflow_extraContext\":{\"description\":\"工作流额外的上下文参数(如环境变量等),可以是任何类型,非必填\"}},\"required\":[]}",
"outputJsonSchema": "{\"type\":\"object\",\"properties\":{\"text\":{\"type\":\"string\"}},\"additionalProperties\":false}",
"lastExecutionTime": null
}
]
}
2026-03-28 12:30:34 - lzwcai_workflow_to_mcp.main - DEBUG - [main.py:124] - API 响应 data 字段: [
{
"id": "2037535460665982977",
"createBy": "duchangyuan",
"createTime": "2026-03-27 22:21:24",
"updateBy": "admin",
"updateTime": "2026-03-28 12:21:49",
"serviceId": "2037535460632428546",
"uniqueName": "银行流水状态检测",
"name": "yinxingliushuizhuangtaijiance_7c4d9a63",
"description": "用于检测流水导入状态,检查是否全部导入完成",
"visualizable": 0,
"toolPrompt": null,
"toolType": "api",
"datasourceId": null,
"sqlTemplate": null,
"sqlParams": "[]",
"resultType": null,
"sourceType": null,
"trainingTaskId": null,
"tableMetadataIds": null,
"executionCount": 0,
"visualizationConfigs": null,
"inputJsonSchema": "{\"type\":\"object\",\"properties\":{\"workflow_extraContext\":{\"description\":\"工作流额外的上下文参数(如环境变量等),可以是任何类型,非必填\"}},\"required\":[]}",
"outputJsonSchema": "{\"type\":\"object\",\"properties\":{\"text\":{\"type\":\"string\"}},\"additionalProperties\":false}",
"lastExecutionTime": null
}
]
2026-03-28 12:30:34 - lzwcai_workflow_to_mcp.main - INFO - [main.py:135] - 从 API 加载工作流配置成功工作流ID: 2037527155235225601, 配置数量: 1
2026-03-28 12:30:34 - lzwcai_workflow_to_mcp.main - INFO - [main.py:165] - 已加载 1 个工具配置
2026-03-28 12:30:34 - lzwcai_workflow_to_mcp.main - INFO - [main.py:352] - 开始运行 MCP Server (stdio 模式)
2026-03-28 12:30:34 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor
2026-03-28 12:30:34 - mcp.server.lowlevel.server - DEBUG - [server.py:675] - Received message: root=InitializedNotification(method='notifications/initialized', params=None, jsonrpc='2.0')
2026-03-28 12:30:40 - mcp.server.lowlevel.server - DEBUG - [server.py:675] - Received message: <mcp.shared.session.RequestResponder object at 0x0000016702D378C0>
2026-03-28 12:30:40 - mcp.server.lowlevel.server - INFO - [server.py:720] - Processing request of type ListToolsRequest
2026-03-28 12:30:40 - mcp.server.lowlevel.server - DEBUG - [server.py:723] - Dispatching request of type ListToolsRequest
2026-03-28 12:30:40 - lzwcai_workflow_to_mcp.main - INFO - [main.py:171] - 收到 ListTools 请求,当前配置数量: 1
2026-03-28 12:30:40 - lzwcai_workflow_to_mcp.main - DEBUG - [main.py:178] - 处理工具配置: name=yinxingliushuizhuangtaijiance_7c4d9a63, description=用于检测流水导入状态,检查是否全部导入完成...
2026-03-28 12:30:40 - lzwcai_workflow_to_mcp.main - DEBUG - [main.py:185] - 从 sqlParams 转换的 inputSchema: {"type": "object", "properties": {}, "required": []}
2026-03-28 12:30:40 - lzwcai_workflow_to_mcp.main - INFO - [main.py:237] - ListTools 响应: 返回 1 个工具
2026-03-28 12:30:40 - mcp.server.lowlevel.server - DEBUG - [server.py:790] - Response sent

View File

@@ -0,0 +1,137 @@
2026-03-28 12:30:33 - root - INFO - [logger_config.py:117] - 日志系统初始化完成 - 日志目录: E:\yh-ai\project\lzwcai-szyg\lzwcai-mcp-server-package\lzwcai_workflow_to_mcp\lzwcai_workflow_to_mcp\logs
2026-03-28 12:30:33 - mcp.server.lowlevel.server - DEBUG - [server.py:162] - Initializing server 'workflow_mcp_server'
2026-03-28 12:30:33 - mcp.server.lowlevel.server - DEBUG - [server.py:439] - Registering handler for ListToolsRequest
2026-03-28 12:30:33 - mcp.server.lowlevel.server - DEBUG - [server.py:519] - Registering handler for CallToolRequest
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.main - INFO - [main.py:334] - ==================================================
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.main - INFO - [main.py:335] - Workflow MCP Server 启动
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.main - INFO - [main.py:336] - ==================================================
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.main - INFO - [main.py:341] - 命令行参数: {'mode': 'api', 'json_path': None, 'workflow_id': None}
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.main - INFO - [main.py:344] - 使用模式: api
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.main - DEBUG - [main.py:103] - ApiLoader 初始化工作流ID: 2037527155235225601
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.main - INFO - [main.py:111] - 开始从 API 加载工作流配置工作流ID: 2037527155235225601
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.utils.api_client - INFO - [api_client.py:115] - [客户端初始化] base_url=http://192.168.2.236:8088
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.utils.api_client - INFO - [api_client.py:116] - [客户端初始化] token=wf_bd39a583670c42ceab48b3353bf2ba43
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.utils.api_client - INFO - [api_client.py:117] - [客户端初始化] execute_timeout=600.0s
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.utils.api_client - INFO - [api_client.py:148] - [API请求] GET http://192.168.2.236:8088/system/workflowManage/getByWorkflowId/2037527155235225601
2026-03-28 12:30:33 - lzwcai_workflow_to_mcp.utils.api_client - DEBUG - [api_client.py:149] - [API请求] Headers: {'X-API-Key': 'wf_bd39a583670c42ceab48b3353bf2ba43'}
2026-03-28 12:30:34 - httpcore.connection - DEBUG - [_trace.py:47] - connect_tcp.started host='192.168.2.236' port=8088 local_address=None timeout=30.0 socket_options=None
2026-03-28 12:30:34 - httpcore.connection - DEBUG - [_trace.py:47] - connect_tcp.complete return_value=<httpcore._backends.sync.SyncStream object at 0x0000016702CF9970>
2026-03-28 12:30:34 - httpcore.http11 - DEBUG - [_trace.py:47] - send_request_headers.started request=<Request [b'GET']>
2026-03-28 12:30:34 - httpcore.http11 - DEBUG - [_trace.py:47] - send_request_headers.complete
2026-03-28 12:30:34 - httpcore.http11 - DEBUG - [_trace.py:47] - send_request_body.started request=<Request [b'GET']>
2026-03-28 12:30:34 - httpcore.http11 - DEBUG - [_trace.py:47] - send_request_body.complete
2026-03-28 12:30:34 - httpcore.http11 - DEBUG - [_trace.py:47] - receive_response_headers.started request=<Request [b'GET']>
2026-03-28 12:30:34 - httpcore.http11 - DEBUG - [_trace.py:47] - receive_response_headers.complete return_value=(b'HTTP/1.1', 200, b'', [(b'Vary', b'Origin'), (b'Vary', b'Access-Control-Request-Method'), (b'Vary', b'Access-Control-Request-Headers'), (b'X-Content-Type-Options', b'nosniff'), (b'X-XSS-Protection', b'1; mode=block'), (b'X-Frame-Options', b'SAMEORIGIN'), (b'Content-Type', b'application/json'), (b'Transfer-Encoding', b'chunked'), (b'Date', b'Sat, 28 Mar 2026 04:30:30 GMT'), (b'Keep-Alive', b'timeout=60'), (b'Connection', b'keep-alive')])
2026-03-28 12:30:34 - httpx - INFO - [_client.py:1025] - HTTP Request: GET http://192.168.2.236:8088/system/workflowManage/getByWorkflowId/2037527155235225601 "HTTP/1.1 200 "
2026-03-28 12:30:34 - httpcore.http11 - DEBUG - [_trace.py:47] - receive_response_body.started request=<Request [b'GET']>
2026-03-28 12:30:34 - httpcore.http11 - DEBUG - [_trace.py:47] - receive_response_body.complete
2026-03-28 12:30:34 - httpcore.http11 - DEBUG - [_trace.py:47] - response_closed.started
2026-03-28 12:30:34 - httpcore.http11 - DEBUG - [_trace.py:47] - response_closed.complete
2026-03-28 12:30:34 - lzwcai_workflow_to_mcp.utils.api_client - INFO - [api_client.py:156] - [API响应] HTTP 200
2026-03-28 12:30:34 - lzwcai_workflow_to_mcp.utils.api_client - DEBUG - [api_client.py:157] - [API响应] Headers: {'vary': 'Origin, Access-Control-Request-Method, Access-Control-Request-Headers', 'x-content-type-options': 'nosniff', 'x-xss-protection': '1; mode=block', 'x-frame-options': 'SAMEORIGIN', 'content-type': 'application/json', 'transfer-encoding': 'chunked', 'date': 'Sat, 28 Mar 2026 04:30:30 GMT', 'keep-alive': 'timeout=60', 'connection': 'keep-alive'}
2026-03-28 12:30:34 - lzwcai_workflow_to_mcp.utils.api_client - INFO - [api_client.py:162] - [API响应] 获取工作流配置成功: workflow_id=2037527155235225601
2026-03-28 12:30:34 - lzwcai_workflow_to_mcp.utils.api_client - DEBUG - [api_client.py:163] - [API响应] Body: {
"msg": "查询成功",
"code": 200,
"data": [
{
"id": "2037535460665982977",
"createBy": "duchangyuan",
"createTime": "2026-03-27 22:21:24",
"updateBy": "admin",
"updateTime": "2026-03-28 12:21:49",
"serviceId": "2037535460632428546",
"uniqueName": "银行流水状态检测",
"name": "yinxingliushuizhuangtaijiance_7c4d9a63",
"description": "用于检测流水导入状态,检查是否全部导入完成",
"visualizable": 0,
"toolPrompt": null,
"toolType": "api",
"datasourceId": null,
"sqlTemplate": null,
"sqlParams": "[]",
"resultType": null,
"sourceType": null,
"trainingTaskId": null,
"tableMetadataIds": null,
"executionCount": 0,
"visualizationConfigs": null,
"inputJsonSchema": "{\"type\":\"object\",\"properties\":{\"workflow_extraContext\":{\"description\":\"工作流额外的上下文参数(如环境变量等),可以是任何类型,非必填\"}},\"required\":[]}",
"outputJsonSchema": "{\"type\":\"object\",\"properties\":{\"text\":{\"type\":\"string\"}},\"additionalProperties\":false}",
"lastExecutionTime": null
}
]
}
2026-03-28 12:30:34 - lzwcai_workflow_to_mcp.main - DEBUG - [main.py:116] - API 响应原始数据: {
"msg": "查询成功",
"code": 200,
"data": [
{
"id": "2037535460665982977",
"createBy": "duchangyuan",
"createTime": "2026-03-27 22:21:24",
"updateBy": "admin",
"updateTime": "2026-03-28 12:21:49",
"serviceId": "2037535460632428546",
"uniqueName": "银行流水状态检测",
"name": "yinxingliushuizhuangtaijiance_7c4d9a63",
"description": "用于检测流水导入状态,检查是否全部导入完成",
"visualizable": 0,
"toolPrompt": null,
"toolType": "api",
"datasourceId": null,
"sqlTemplate": null,
"sqlParams": "[]",
"resultType": null,
"sourceType": null,
"trainingTaskId": null,
"tableMetadataIds": null,
"executionCount": 0,
"visualizationConfigs": null,
"inputJsonSchema": "{\"type\":\"object\",\"properties\":{\"workflow_extraContext\":{\"description\":\"工作流额外的上下文参数(如环境变量等),可以是任何类型,非必填\"}},\"required\":[]}",
"outputJsonSchema": "{\"type\":\"object\",\"properties\":{\"text\":{\"type\":\"string\"}},\"additionalProperties\":false}",
"lastExecutionTime": null
}
]
}
2026-03-28 12:30:34 - lzwcai_workflow_to_mcp.main - DEBUG - [main.py:124] - API 响应 data 字段: [
{
"id": "2037535460665982977",
"createBy": "duchangyuan",
"createTime": "2026-03-27 22:21:24",
"updateBy": "admin",
"updateTime": "2026-03-28 12:21:49",
"serviceId": "2037535460632428546",
"uniqueName": "银行流水状态检测",
"name": "yinxingliushuizhuangtaijiance_7c4d9a63",
"description": "用于检测流水导入状态,检查是否全部导入完成",
"visualizable": 0,
"toolPrompt": null,
"toolType": "api",
"datasourceId": null,
"sqlTemplate": null,
"sqlParams": "[]",
"resultType": null,
"sourceType": null,
"trainingTaskId": null,
"tableMetadataIds": null,
"executionCount": 0,
"visualizationConfigs": null,
"inputJsonSchema": "{\"type\":\"object\",\"properties\":{\"workflow_extraContext\":{\"description\":\"工作流额外的上下文参数(如环境变量等),可以是任何类型,非必填\"}},\"required\":[]}",
"outputJsonSchema": "{\"type\":\"object\",\"properties\":{\"text\":{\"type\":\"string\"}},\"additionalProperties\":false}",
"lastExecutionTime": null
}
]
2026-03-28 12:30:34 - lzwcai_workflow_to_mcp.main - INFO - [main.py:135] - 从 API 加载工作流配置成功工作流ID: 2037527155235225601, 配置数量: 1
2026-03-28 12:30:34 - lzwcai_workflow_to_mcp.main - INFO - [main.py:165] - 已加载 1 个工具配置
2026-03-28 12:30:34 - lzwcai_workflow_to_mcp.main - INFO - [main.py:352] - 开始运行 MCP Server (stdio 模式)
2026-03-28 12:30:34 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor
2026-03-28 12:30:34 - mcp.server.lowlevel.server - DEBUG - [server.py:675] - Received message: root=InitializedNotification(method='notifications/initialized', params=None, jsonrpc='2.0')
2026-03-28 12:30:40 - mcp.server.lowlevel.server - DEBUG - [server.py:675] - Received message: <mcp.shared.session.RequestResponder object at 0x0000016702D378C0>
2026-03-28 12:30:40 - mcp.server.lowlevel.server - INFO - [server.py:720] - Processing request of type ListToolsRequest
2026-03-28 12:30:40 - mcp.server.lowlevel.server - DEBUG - [server.py:723] - Dispatching request of type ListToolsRequest
2026-03-28 12:30:40 - lzwcai_workflow_to_mcp.main - INFO - [main.py:171] - 收到 ListTools 请求,当前配置数量: 1
2026-03-28 12:30:40 - lzwcai_workflow_to_mcp.main - DEBUG - [main.py:178] - 处理工具配置: name=yinxingliushuizhuangtaijiance_7c4d9a63, description=用于检测流水导入状态,检查是否全部导入完成...
2026-03-28 12:30:40 - lzwcai_workflow_to_mcp.main - DEBUG - [main.py:185] - 从 sqlParams 转换的 inputSchema: {"type": "object", "properties": {}, "required": []}
2026-03-28 12:30:40 - lzwcai_workflow_to_mcp.main - INFO - [main.py:237] - ListTools 响应: 返回 1 个工具
2026-03-28 12:30:40 - mcp.server.lowlevel.server - DEBUG - [server.py:790] - Response sent

View File

@@ -19,12 +19,12 @@ from mcp.server.models import InitializationOptions
from mcp.server.stdio import stdio_server from mcp.server.stdio import stdio_server
try: try:
from .schema_converter import convert_sql_params_to_input_schema from .schema_converter import convert_sql_params_to_input_schema, sanitize_json_schema
from .utils.api_client import execute_workflow, get_workflow_by_id, extract_final_output from .utils.api_client import execute_workflow, get_workflow_by_id, extract_final_output
from .utils.env_config import get_workflow_id from .utils.env_config import get_workflow_id
from .utils.logger_config import setup_system_logging, get_logger from .utils.logger_config import setup_system_logging, get_logger
except ImportError: except ImportError:
from schema_converter import convert_sql_params_to_input_schema from schema_converter import convert_sql_params_to_input_schema, sanitize_json_schema
from utils.api_client import execute_workflow, get_workflow_by_id, extract_final_output from utils.api_client import execute_workflow, get_workflow_by_id, extract_final_output
from utils.env_config import get_workflow_id from utils.env_config import get_workflow_id
from utils.logger_config import setup_system_logging, get_logger from utils.logger_config import setup_system_logging, get_logger
@@ -174,13 +174,57 @@ async def handle_list_tools() -> list[types.Tool]:
for query in _tools_config: for query in _tools_config:
name = query.get("name", "") name = query.get("name", "")
description = query.get("description") or query.get("toolPrompt") or query.get("uniqueName", "") description = query.get("description") or query.get("toolPrompt") or query.get("uniqueName", "")
sql_params = query.get("sqlParams", "[]")
logger.debug(f"处理工具配置: name={name}, description={description[:50] if description else 'None'}...") logger.debug(f"处理工具配置: name={name}, description={description[:50] if description else 'None'}...")
# 转换参数为 inputSchema # 优先从 sqlParams 转换,因为它包含更详细的文件配置信息
input_schema = convert_sql_params_to_input_schema(sql_params) sql_params = query.get("sqlParams")
logger.debug(f"工具 {name} 的 inputSchema: {json.dumps(input_schema, ensure_ascii=False)}") if sql_params:
try:
input_schema = convert_sql_params_to_input_schema(sql_params)
logger.debug(f"从 sqlParams 转换的 inputSchema: {json.dumps(input_schema, ensure_ascii=False)}")
except Exception as e:
logger.warning(f"从 sqlParams 转换失败: {e},尝试使用 inputJsonSchema")
# 回退到 inputJsonSchema
input_json_schema = query.get("inputJsonSchema")
if input_json_schema:
try:
if isinstance(input_json_schema, str):
input_schema = json.loads(input_json_schema)
else:
input_schema = input_json_schema
input_schema = sanitize_json_schema(input_schema)
logger.debug(f"使用 inputJsonSchema (已清理): {json.dumps(input_schema, ensure_ascii=False)}")
except (json.JSONDecodeError, TypeError) as e2:
logger.error(f"解析 inputJsonSchema 也失败: {e2},使用空 schema")
input_schema = {"type": "object", "properties": {}, "required": []}
else:
logger.warning("sqlParams 和 inputJsonSchema 都不可用,使用空 schema")
input_schema = {"type": "object", "properties": {}, "required": []}
else:
# 如果没有 sqlParams尝试使用 inputJsonSchema
input_json_schema = query.get("inputJsonSchema")
if input_json_schema:
try:
if isinstance(input_json_schema, str):
input_schema = json.loads(input_json_schema)
else:
input_schema = input_json_schema
input_schema = sanitize_json_schema(input_schema)
logger.debug(f"使用 inputJsonSchema (已清理): {json.dumps(input_schema, ensure_ascii=False)}")
except (json.JSONDecodeError, TypeError) as e:
logger.error(f"解析 inputJsonSchema 失败: {e},使用空 schema")
input_schema = {"type": "object", "properties": {}, "required": []}
else:
logger.warning("sqlParams 和 inputJsonSchema 都不存在,使用空 schema")
input_schema = {"type": "object", "properties": {}, "required": []}
# 添加 workflow_extraContext 字段到 schema可以接收任何类型非必填
if "properties" not in input_schema:
input_schema["properties"] = {}
input_schema["properties"]["workflow_extraContext"] = {
"description": "工作流额外的上下文参数(如环境变量等),可以是任何类型,非必填"
}
tools.append( tools.append(
types.Tool( types.Tool(
@@ -197,7 +241,7 @@ async def handle_list_tools() -> list[types.Tool]:
@server.call_tool() @server.call_tool()
async def handle_call_tool( async def handle_call_tool(
name: str, name: str,
arguments: dict | None arguments: dict | None,
) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]: ) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
"""调用工具""" """调用工具"""
logger.info(f"收到 CallTool 请求: name={name}, arguments={json.dumps(arguments, ensure_ascii=False) if arguments else 'None'}") logger.info(f"收到 CallTool 请求: name={name}, arguments={json.dumps(arguments, ensure_ascii=False) if arguments else 'None'}")
@@ -219,10 +263,25 @@ async def handle_call_tool(
workflow_id = tool_config.get("workflowId") or get_workflow_id() workflow_id = tool_config.get("workflowId") or get_workflow_id()
logger.info(f"使用工作流ID: {workflow_id}") logger.info(f"使用工作流ID: {workflow_id}")
# 提取 workflow_extraContext 字段并合并到 inputs
inputs = arguments or {}
workflow_extra_context = inputs.pop("workflow_extraContext", None)
# 如果 workflow_extraContext 存在,将其内容合并到 inputs
if workflow_extra_context is not None:
if isinstance(workflow_extra_context, dict):
# 如果 workflow_extraContext 是字典,合并到 inputs
inputs.update(workflow_extra_context)
logger.info(f"workflow_extraContext 是字典类型,已合并到 inputs: {json.dumps(workflow_extra_context, ensure_ascii=False)}")
else:
# 如果 workflow_extraContext 不是字典,作为 workflow_extraContext 字段保留
inputs["workflow_extraContext"] = workflow_extra_context
logger.info(f"workflow_extraContext 是 {type(workflow_extra_context).__name__} 类型,保留为 workflow_extraContext 字段")
# 构建请求数据 # 构建请求数据
request_data = { request_data = {
"workflowId": workflow_id, "workflowId": workflow_id,
"inputs": arguments or {} "inputs": inputs
} }
logger.info(f"执行工作流请求数据: {json.dumps(request_data, ensure_ascii=False, indent=2)}") logger.info(f"执行工作流请求数据: {json.dumps(request_data, ensure_ascii=False, indent=2)}")

View File

@@ -7,11 +7,48 @@ Schema 转换器
- paragraph: 段落/多行文本 - paragraph: 段落/多行文本
- select: 下拉选项 - select: 下拉选项
- number: 数字输入 - number: 数字输入
- file: 单文件上传
- fileList: 多文件上传
""" """
import json import json
from typing import Any from typing import Any
def sanitize_json_schema(schema: dict) -> dict:
"""
清理 JSON Schema确保 enum 字段只包含字符串值
Args:
schema: 原始 JSON Schema
Returns:
dict: 清理后的 JSON Schema
"""
if not isinstance(schema, dict):
return schema
# 递归处理 properties
if "properties" in schema and isinstance(schema["properties"], dict):
for prop_name, prop_schema in schema["properties"].items():
if isinstance(prop_schema, dict) and "enum" in prop_schema:
enum_values = prop_schema["enum"]
if isinstance(enum_values, list) and len(enum_values) > 0:
# 检查是否是对象数组
if isinstance(enum_values[0], dict):
# 提取 value 字段
cleaned_enum = [
item.get("value", item.get("label", ""))
for item in enum_values
if isinstance(item, dict)
]
# 过滤空值
prop_schema["enum"] = [v for v in cleaned_enum if v]
# 保留原始数据
prop_schema["x-options"] = enum_values
return schema
def convert_param_to_schema_property(param: dict) -> tuple[str, dict, bool]: def convert_param_to_schema_property(param: dict) -> tuple[str, dict, bool]:
""" """
将单个参数转换为 JSON Schema property 将单个参数转换为 JSON Schema property
@@ -25,7 +62,13 @@ def convert_param_to_schema_property(param: dict) -> tuple[str, dict, bool]:
"maxLength": 200, "maxLength": 200,
"defaultValue": "", "defaultValue": "",
"required": true, "required": true,
"options": ["选项1", "选项2"] # 仅 select 类型 "options": ["选项1", "选项2"], # 仅 select 类型
"fileConfig": { # 仅 file/fileList 类型
"uploadMode": "both",
"typeCategories": ["image"],
"customAccept": "",
"accept": ".jpg,.jpeg,.png"
}
} }
Returns: Returns:
@@ -38,6 +81,7 @@ def convert_param_to_schema_property(param: dict) -> tuple[str, dict, bool]:
max_length = param.get("maxLength") max_length = param.get("maxLength")
is_required = param.get("required", False) is_required = param.get("required", False)
options = param.get("options", []) options = param.get("options", [])
file_config = param.get("fileConfig", {})
property_schema = { property_schema = {
"description": display_name "description": display_name
@@ -57,17 +101,69 @@ def convert_param_to_schema_property(param: dict) -> tuple[str, dict, bool]:
elif param_type == "select": elif param_type == "select":
property_schema["type"] = "string" property_schema["type"] = "string"
if options: if options:
property_schema["enum"] = options # 处理 options 可能是对象数组 [{label, value}] 或字符串数组的情况
if isinstance(options, list) and len(options) > 0:
if isinstance(options[0], dict):
# 对象数组,提取 value 字段作为 enum过滤空值
enum_values = [
opt.get("value", opt.get("label", ""))
for opt in options
if isinstance(opt, dict)
]
# 过滤掉空字符串
property_schema["enum"] = [v for v in enum_values if v]
# 保留原始 options 供前端使用(如果前端支持)
property_schema["x-options"] = options
else:
# 字符串数组,直接使用,过滤空值
property_schema["enum"] = [v for v in options if v]
elif param_type == "number": elif param_type == "number":
property_schema["type"] = "number" property_schema["type"] = "number"
elif param_type == "file":
# 单文件上传
property_schema["type"] = "string"
property_schema["format"] = "url"
property_schema["x-file-type"] = "single"
# 添加文件配置信息
if file_config:
if file_config.get("accept"):
property_schema["x-accept"] = file_config["accept"]
if file_config.get("typeCategories"):
property_schema["x-type-categories"] = file_config["typeCategories"]
if file_config.get("uploadMode"):
property_schema["x-upload-mode"] = file_config["uploadMode"]
# 保留完整配置
property_schema["x-file-config"] = file_config
elif param_type == "fileList":
# 多文件上传
property_schema["type"] = "array"
property_schema["items"] = {
"type": "string",
"format": "url"
}
property_schema["x-file-type"] = "multiple"
# 添加文件配置信息
if file_config:
if file_config.get("accept"):
property_schema["x-accept"] = file_config["accept"]
if file_config.get("typeCategories"):
property_schema["x-type-categories"] = file_config["typeCategories"]
if file_config.get("uploadMode"):
property_schema["x-upload-mode"] = file_config["uploadMode"]
# 保留完整配置
property_schema["x-file-config"] = file_config
else: else:
# 默认当作 string 处理 # 默认当作 string 处理
property_schema["type"] = "string" property_schema["type"] = "string"
# 添加默认值 # 添加默认值(文件类型不需要默认值)
if default_value not in (None, ""): if default_value not in (None, "") and param_type not in ("file", "fileList"):
if param_type == "number": if param_type == "number":
try: try:
property_schema["default"] = int(default_value) if str(default_value).isdigit() else float(default_value) property_schema["default"] = int(default_value) if str(default_value).isdigit() else float(default_value)

View File

@@ -7,8 +7,8 @@ import os
if __name__ == "__main__": if __name__ == "__main__":
# 设置环境变量 # 设置环境变量
os.environ["workflowId"] = "2005892514011795457" os.environ["workflowId"] = "2037527155235225601"
os.environ["workflowExecuteKey"] = "wf_ce270212b2ee45ab9c81714a7c243c56" os.environ["workflowExecuteKey"] = "wf_bd39a583670c42ceab48b3353bf2ba43"
os.environ["backendBaseUrl"] = "http://192.168.2.236:8088" os.environ["backendBaseUrl"] = "http://192.168.2.236:8088"
# Import and run the actual MCP server # Import and run the actual MCP server

View File

@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "lzwcai-workflow-to-mcp" name = "lzwcai-workflow-to-mcp"
version = "0.1.3" version = "0.1.8"
description = "MCP server for executing business SQL queries with dynamic tool generation" description = "MCP server for executing business SQL queries with dynamic tool generation"
readme = "README.md" readme = "README.md"
requires-python = ">=3.10" requires-python = ">=3.10"