feat(lark-mcp): 重构资产确认卡片并更新项目配置

重构 send_asset_confirmation_card 工具,使用新的卡片模板和参数结构
- 将卡片布局从 column_set 改为 form,简化结构
- 更新参数:inputs/outputs 合并为 asset_list,新增 order_number 和 remark
- 卡片交互按钮现在传递 order_number 和 user_id 到回调
- 更新工具描述和必填字段

同时更新 MCP 服务器配置:
- 统一重命名项目为 lzwcai-lark-mcp 和 lzwcai-file-tools-mcp
- 更新版本号和配置文件中的命令名称
- 更新 file-tools 的 MinIO 环境变量配置
This commit is contained in:
bin
2026-03-03 11:34:45 +08:00
parent 712d9b9aa5
commit fa226733b8
6 changed files with 148 additions and 155 deletions

View File

@@ -1,17 +1,17 @@
{ {
"mcpServers": { "mcpServers": {
"lzwcai-mcpskills-file-tools-mcp": { "lzwcai-file-tools-mcp": {
"disabled": false, "disabled": false,
"type": "stdio", "type": "stdio",
"timeout": 30, "timeout": 30,
"command": "uvx", "command": "uvx",
"args": [ "args": [
"lzwcai-mcpskills-file-tools-mcp" "lzwcai-file-tools-mcp"
], ],
"env": { "env": {
"minio_endpoint": "http://sceneminios3.lzwcai.com:9000", "minio_endpoint": "http://192.168.11.24:9000",
"minio_access_key": "TgPBBz0OdlvEVzG3", "minio_access_key": "cXk8WPR3ix86J9aGK6tH",
"minio_secret_key": "AgpliEB6L7UWXXeBaAN0gL4xiRCGCE03" "minio_secret_key": "FSH8g3tx8bTR4w8BZmwl35WvWbOXZvfvCcivRRJE"
} }
} }
} }

View File

@@ -3,8 +3,8 @@ requires = ["hatchling"]
build-backend = "hatchling.build" build-backend = "hatchling.build"
[project] [project]
name = "lzwcai-mcpskills-file-tools-mcp" name = "lzwcai-file-tools-mcp"
version = "0.1.12" version = "0.1.0"
description = "File tools MCP server" description = "File tools MCP server"
requires-python = ">=3.10" requires-python = ">=3.10"
dependencies = [ dependencies = [
@@ -15,7 +15,7 @@ dependencies = [
] ]
[project.scripts] [project.scripts]
lzwcai-mcpskills-file-tools-mcp = "file_tools.main:main" lzwcai-file-tools-mcp = "file_tools.main:main"
[tool.hatch.build.targets.wheel] [tool.hatch.build.targets.wheel]
packages = ["file_tools"] packages = ["file_tools"]

View File

@@ -15,57 +15,30 @@
"body": { "body": {
"direction": "vertical", "direction": "vertical",
"elements": [ "elements": [
{
"tag": "column_set",
"flex_mode": "stretch",
"horizontal_spacing": "12px",
"horizontal_align": "left",
"columns": [
{
"tag": "column",
"width": "weighted",
"elements": [
{
"tag": "markdown",
"content": "**<font color='blue-600'>用户:</font>** <person id=${user_id} show_name=true show_avatar=true style='normal'></person>",
"text_align": "left",
"text_size": "normal"
}
],
"vertical_spacing": "8px",
"horizontal_align": "left",
"vertical_align": "top",
"weight": 1
},
{
"tag": "column",
"width": "weighted",
"elements": [
{
"tag": "markdown",
"content": "**<font color='blue-600'>发生时间:</font>** <font color='grey'>${change_time}</font>",
"text_align": "left",
"text_size": "normal"
}
],
"vertical_spacing": "8px",
"horizontal_align": "left",
"vertical_align": "top",
"weight": 1
}
],
"margin": "0px 0px 0px 0px"
},
{
"tag": "hr",
"margin": "0px 0px 0px 0px"
},
{ {
"tag": "form", "tag": "form",
"elements": [ "elements": [
{ {
"tag": "markdown", "tag": "markdown",
"content": "**<font color='blue-600'>\\*请准确选择属于您的变动项:</font>**", "content": "**<font color='blue-600'>确认单号:</font>** <font color='grey'>${order_number}</font>",
"text_align": "left",
"text_size": "normal"
},
{
"tag": "markdown",
"content": "**<font color='blue-600'>用户:</font>** <person id=${user_id} show_name=true show_avatar=true style='normal'></person>",
"text_align": "left",
"text_size": "normal"
},
{
"tag": "markdown",
"content": "**<font color='blue-600'>发生时间:</font>** <font color='grey'>${change_time}</font>",
"text_align": "left",
"text_size": "normal"
},
{
"tag": "markdown",
"content": "**<font color='blue-600'>\\*请准确选择关于您的变动项:</font>**",
"text_align": "left", "text_align": "left",
"text_size": "normal", "text_size": "normal",
"margin": "0px 0px 8px 0px" "margin": "0px 0px 8px 0px"
@@ -76,29 +49,18 @@
"tag": "plain_text", "tag": "plain_text",
"content": "请选择资产变动项" "content": "请选择资产变动项"
}, },
"options": [ "options": "${asset_list}",
{
"text": {
"tag": "plain_text",
"content": "资产项1"
},
"value": "item1"
},
{
"text": {
"tag": "plain_text",
"content": "资产项2"
},
"value": "item2"
}
],
"type": "default", "type": "default",
"width": "fill", "width": "fill",
"required": false, "required": false,
"name": "asset_changes", "name": "input_assets",
"margin": "0px 0px 16px 0px", "margin": "0px 0px 16px 0px",
"element_id": "cIiptD7Z4hCtAeR5Rb0b" "element_id": "cIiptD7Z4hCtAeR5Rb0b"
}, },
{
"tag": "hr",
"margin": "0px 0px 0px 0px"
},
{ {
"tag": "markdown", "tag": "markdown",
"content": "**<font color='blue-600'>其他说明:</font>**", "content": "**<font color='blue-600'>其他说明:</font>**",
@@ -114,7 +76,7 @@
}, },
"default_value": "", "default_value": "",
"width": "fill", "width": "fill",
"name": "Input_3h27n7woqci", "name": "input_remark",
"margin": "0px 0px 0px 0px" "margin": "0px 0px 0px 0px"
}, },
{ {
@@ -140,7 +102,7 @@
{ {
"type": "callback", "type": "callback",
"value": { "value": {
"action": "confirm_asset_changes" "action": "card.action.trigger"
} }
} }
], ],
@@ -168,7 +130,7 @@
"confirm": { "confirm": {
"title": { "title": {
"tag": "plain_text", "tag": "plain_text",
"content": "反馈并废除该确认单吗?" "content": "反馈误报并废除该确认单吗?"
}, },
"text": { "text": {
"tag": "plain_text", "tag": "plain_text",
@@ -179,7 +141,7 @@
{ {
"type": "callback", "type": "callback",
"value": { "value": {
"": "" "action": "card.action.trigger"
} }
} }
], ],

View File

@@ -2,7 +2,7 @@ import json
import mimetypes import mimetypes
import os import os
import re import re
from datetime import datetime from datetime import datetime, timezone, timedelta
from typing import Dict, List from typing import Dict, List
from urllib.parse import urlparse from urllib.parse import urlparse
@@ -77,7 +77,7 @@ tools = [
), ),
Tool( Tool(
name="send_asset_confirmation_card", name="send_asset_confirmation_card",
description="发送资产变动确认卡片入参为user_id、time、inputs和outputs", description="发送定制资产确认卡片入参为user_id、order_number、change_time、asset_list和remark",
inputSchema={ inputSchema={
"type": "object", "type": "object",
"properties": { "properties": {
@@ -85,26 +85,27 @@ tools = [
"type": "string", "type": "string",
"description": "消息接收者ID同时用于卡片内person组件" "description": "消息接收者ID同时用于卡片内person组件"
}, },
"time": { "order_number": {
"type": "string",
"description": "确认单号"
},
"change_time": {
"type": "string", "type": "string",
"description": "资产变动发生时间" "description": "资产变动发生时间"
}, },
"inputs": { "asset_list": {
"type": "array", "type": "array",
"description": "入库资产列表", "description": "可选择的资产变动项列表",
"items": { "items": {
"type": "string" "type": "object"
} }
}, },
"outputs": { "remark": {
"type": "array", "type": "string",
"description": "出库资产列表", "description": "反馈确认弹窗内容"
"items": {
"type": "string"
}
} }
}, },
"required": ["user_id", "time"] "required": ["user_id", "order_number"]
} }
) )
] ]
@@ -280,16 +281,53 @@ def _build_asset_options(inputs: List[str], outputs: List[str]) -> tuple[list[Di
return options, "".join(labels) return options, "".join(labels)
def send_asset_confirmation_card(token: str, user_id: str, change_time: str, inputs: object, outputs: object) -> str: def _build_select_options(items: List[str]) -> list[Dict[str, object]]:
options: list[Dict[str, object]] = []
for item in items:
label = str(item).strip()
if not label:
continue
options.append({
"text": {"tag": "plain_text", "content": label},
"value": label
})
return options
def send_asset_confirmation_card(
token: str,
user_id: str,
order_number: str,
change_time: str,
asset_list: object,
remark: object = ""
) -> str:
normalized_user_id = str(user_id).strip() normalized_user_id = str(user_id).strip()
if not normalized_user_id: if not normalized_user_id:
raise ValueError("missing user_id") raise ValueError("missing user_id")
normalized_order_number = str(order_number).strip()
if not normalized_order_number:
raise ValueError("missing order_number")
normalized_time = str(change_time or "").strip() normalized_time = str(change_time or "").strip()
if not normalized_time: if not normalized_time:
normalized_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") normalized_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
input_items = _normalize_asset_items(inputs) normalized_remark = str(remark or "").strip()
output_items = _normalize_asset_items(outputs)
options, summary = _build_asset_options(input_items, output_items) options = []
if isinstance(asset_list, list):
for item in asset_list:
if isinstance(item, dict):
for k, v in item.items():
options.append({
"text": {"tag": "plain_text", "content": str(k)},
"value": str(v)
})
elif isinstance(item, str):
options.append({
"text": {"tag": "plain_text", "content": item},
"value": item
})
content = { content = {
"schema": "2.0", "schema": "2.0",
"config": { "config": {
@@ -307,57 +345,30 @@ def send_asset_confirmation_card(token: str, user_id: str, change_time: str, inp
"body": { "body": {
"direction": "vertical", "direction": "vertical",
"elements": [ "elements": [
{
"tag": "column_set",
"flex_mode": "stretch",
"horizontal_spacing": "12px",
"horizontal_align": "left",
"columns": [
{
"tag": "column",
"width": "weighted",
"elements": [
{
"tag": "markdown",
"content": f"**<font color='blue-600'>用户:</font>** <person id={normalized_user_id} show_name=true show_avatar=true style='normal'></person>",
"text_align": "left",
"text_size": "normal"
}
],
"vertical_spacing": "8px",
"horizontal_align": "left",
"vertical_align": "top",
"weight": 1
},
{
"tag": "column",
"width": "weighted",
"elements": [
{
"tag": "markdown",
"content": f"**<font color='blue-600'>发生时间:</font>** <font color='grey'>{normalized_time}</font>",
"text_align": "left",
"text_size": "normal"
}
],
"vertical_spacing": "8px",
"horizontal_align": "left",
"vertical_align": "top",
"weight": 1
}
],
"margin": "0px 0px 0px 0px"
},
{
"tag": "hr",
"margin": "0px 0px 0px 0px"
},
{ {
"tag": "form", "tag": "form",
"elements": [ "elements": [
{ {
"tag": "markdown", "tag": "markdown",
"content": "**<font color='blue-600'>\\*请准确选择属于您的变动项:</font>**", "content": f"**<font color='blue-600'>确认单号:</font>** <font color='grey'>{normalized_order_number}</font>",
"text_align": "left",
"text_size": "normal"
},
{
"tag": "markdown",
"content": f"**<font color='blue-600'>用户:</font>** <person id={normalized_user_id} show_name=true show_avatar=true style='normal'></person>",
"text_align": "left",
"text_size": "normal"
},
{
"tag": "markdown",
"content": f"**<font color='blue-600'>发生时间:</font>** <font color='grey'>{normalized_time}</font>",
"text_align": "left",
"text_size": "normal"
},
{
"tag": "markdown",
"content": "**<font color='blue-600'>\\*请准确选择关于您的变动项:</font>**",
"text_align": "left", "text_align": "left",
"text_size": "normal", "text_size": "normal",
"margin": "0px 0px 8px 0px" "margin": "0px 0px 8px 0px"
@@ -372,10 +383,14 @@ def send_asset_confirmation_card(token: str, user_id: str, change_time: str, inp
"type": "default", "type": "default",
"width": "fill", "width": "fill",
"required": False, "required": False,
"name": "asset_changes", "name": "input_assets",
"margin": "0px 0px 16px 0px", "margin": "0px 0px 16px 0px",
"element_id": "cIiptD7Z4hCtAeR5Rb0b" "element_id": "cIiptD7Z4hCtAeR5Rb0b"
}, },
{
"tag": "hr",
"margin": "0px 0px 0px 0px"
},
{ {
"tag": "markdown", "tag": "markdown",
"content": "**<font color='blue-600'>其他说明:</font>**", "content": "**<font color='blue-600'>其他说明:</font>**",
@@ -391,7 +406,7 @@ def send_asset_confirmation_card(token: str, user_id: str, change_time: str, inp
}, },
"default_value": "", "default_value": "",
"width": "fill", "width": "fill",
"name": "Input_3h27n7woqci", "name": "input_remark",
"margin": "0px 0px 0px 0px" "margin": "0px 0px 0px 0px"
}, },
{ {
@@ -417,7 +432,9 @@ def send_asset_confirmation_card(token: str, user_id: str, change_time: str, inp
{ {
"type": "callback", "type": "callback",
"value": { "value": {
"action": "confirm_asset_changes" "action": "card.action.trigger",
"order_number": normalized_order_number,
"user_id": normalized_user_id
} }
} }
], ],
@@ -445,18 +462,20 @@ def send_asset_confirmation_card(token: str, user_id: str, change_time: str, inp
"confirm": { "confirm": {
"title": { "title": {
"tag": "plain_text", "tag": "plain_text",
"content": "反馈并废除该确认单吗?" "content": "反馈误报并废除该确认单吗?"
}, },
"text": { "text": {
"tag": "plain_text", "tag": "plain_text",
"content": summary "content": normalized_remark
} }
}, },
"behaviors": [ "behaviors": [
{ {
"type": "callback", "type": "callback",
"value": { "value": {
"": "" "action": "card.action.trigger",
"order_number": normalized_order_number,
"user_id": normalized_user_id
} }
} }
], ],
@@ -543,7 +562,9 @@ def send_asset_confirmation_card(token: str, user_id: str, change_time: str, inp
def send_card_message(token: str, receiver_id: str, person_id: str, image_key: str) -> str: def send_card_message(token: str, receiver_id: str, person_id: str, image_key: str) -> str:
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") utc_now = datetime.now(timezone.utc)
cst_now = utc_now + timedelta(hours=8)
current_time = cst_now.strftime("%Y-%m-%d %H:%M:%S")
content = { content = {
"schema": "2.0", "schema": "2.0",
"config": { "config": {
@@ -746,12 +767,22 @@ async def handle_call_tool(name: str, arguments: Dict[str, object], token: str)
result = send_card_messages(token, receiver_ids, person_id, image_key) result = send_card_messages(token, receiver_ids, person_id, image_key)
elif name == "send_asset_confirmation_card": elif name == "send_asset_confirmation_card":
user_id = str(arguments.get("user_id", "")).strip() user_id = str(arguments.get("user_id", "")).strip()
change_time = str(arguments.get("time", "")).strip() order_number = str(arguments.get("order_number", "")).strip()
inputs = arguments.get("inputs") change_time = str(arguments.get("change_time", "")).strip()
outputs = arguments.get("outputs") asset_list = arguments.get("asset_list")
remark = str(arguments.get("remark", "")).strip()
if not user_id: if not user_id:
raise ValueError("missing user_id") raise ValueError("missing user_id")
result = send_asset_confirmation_card(token, user_id, change_time, inputs, outputs) if not order_number:
raise ValueError("missing order_number")
result = send_asset_confirmation_card(
token,
user_id,
order_number,
change_time,
asset_list,
remark
)
else: else:
raise ValueError(f"unknown tool name: {name}") raise ValueError(f"unknown tool name: {name}")
return [TextContent(type="text", text=result)] return [TextContent(type="text", text=result)]

View File

@@ -1,12 +1,12 @@
{ {
"mcpServers": { "mcpServers": {
"lzwcai-mcpskills-lark-mcp": { "lzwcai-lark-mcp": {
"disabled": false, "disabled": false,
"type": "stdio", "type": "stdio",
"timeout": 30, "timeout": 30,
"command": "uvx", "command": "uvx",
"args": [ "args": [
"lzwcai-mcpskills-lark-mcp" "lzwcai-lark-mcp"
], ],
"env": { "env": {
"app_id": "cli_a8d0e0c140169013", "app_id": "cli_a8d0e0c140169013",
@@ -14,4 +14,4 @@
} }
} }
} }
} }

View File

@@ -3,8 +3,8 @@ requires = ["hatchling"]
build-backend = "hatchling.build" build-backend = "hatchling.build"
[project] [project]
name = "lzwcai-mcpskills-lark-mcp" name = "lzwcai-lark-mcp"
version = "0.1.10" version = "0.1.3"
description = "Lark MCP server" description = "Lark MCP server"
requires-python = ">=3.10" requires-python = ">=3.10"
dependencies = [ dependencies = [
@@ -15,7 +15,7 @@ dependencies = [
] ]
[project.scripts] [project.scripts]
lzwcai-mcpskills-lark-mcp = "lzwcai_lark_mcp.main:main" lzwcai-lark-mcp = "lzwcai_lark_mcp.main:main"
[tool.hatch.build.targets.wheel] [tool.hatch.build.targets.wheel]
packages = ["lzwcai_lark_mcp"] packages = ["lzwcai_lark_mcp"]