From 09150f94411ce09fdc0e16dfbc2c2b3a1b6a39a7 Mon Sep 17 00:00:00 2001 From: tiehongji Date: Sun, 17 May 2026 18:32:40 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=89=AA=E8=BE=91?= =?UTF-8?q?=E4=BB=A5=E5=8F=8A=E8=A7=86=E9=A2=91=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/mcp_server_mediakit/README.md | 217 +++++ server/mcp_server_mediakit/README_zh.md | 218 +++++ server/mcp_server_mediakit/pyproject.toml | 27 + .../mcp_server_mediakit/src/base/__init__.py | 28 + .../mcp_server_mediakit/src/base/api_info.py | 16 + .../mcp_server_mediakit/src/base/base_mcp.py | 206 +++++ server/mcp_server_mediakit/src/base/client.py | 360 ++++++++ .../mcp_server_mediakit/src/base/constant.py | 25 + .../src/mediakit/mcp_server.py | 34 + .../src/mediakit/mcp_tools/__init__.py | 0 .../src/mediakit/mcp_tools/editing.py | 237 ++++++ .../src/mediakit/mcp_tools/shared.py | 69 ++ .../src/mediakit/mcp_tools/video.py | 79 ++ .../src/mediakit/server.py | 62 ++ .../src/mediakit/utils/__init__.py | 0 .../src/mediakit/utils/async_poller.py | 36 + .../src/mediakit/utils/response.py | 70 ++ server/mcp_server_mediakit/uv.lock | 782 ++++++++++++++++++ 18 files changed, 2466 insertions(+) create mode 100644 server/mcp_server_mediakit/README.md create mode 100644 server/mcp_server_mediakit/README_zh.md create mode 100644 server/mcp_server_mediakit/pyproject.toml create mode 100644 server/mcp_server_mediakit/src/base/__init__.py create mode 100644 server/mcp_server_mediakit/src/base/api_info.py create mode 100644 server/mcp_server_mediakit/src/base/base_mcp.py create mode 100644 server/mcp_server_mediakit/src/base/client.py create mode 100644 server/mcp_server_mediakit/src/base/constant.py create mode 100644 server/mcp_server_mediakit/src/mediakit/mcp_server.py create mode 100644 server/mcp_server_mediakit/src/mediakit/mcp_tools/__init__.py create mode 100644 server/mcp_server_mediakit/src/mediakit/mcp_tools/editing.py create mode 100644 server/mcp_server_mediakit/src/mediakit/mcp_tools/shared.py create mode 100644 server/mcp_server_mediakit/src/mediakit/mcp_tools/video.py create mode 100644 server/mcp_server_mediakit/src/mediakit/server.py create mode 100644 server/mcp_server_mediakit/src/mediakit/utils/__init__.py create mode 100644 server/mcp_server_mediakit/src/mediakit/utils/async_poller.py create mode 100644 server/mcp_server_mediakit/src/mediakit/utils/response.py create mode 100644 server/mcp_server_mediakit/uv.lock diff --git a/server/mcp_server_mediakit/README.md b/server/mcp_server_mediakit/README.md new file mode 100644 index 00000000..8321a5b4 --- /dev/null +++ b/server/mcp_server_mediakit/README.md @@ -0,0 +1,217 @@ +# MediaKit MCP Server + +MediaKit MCP Server is a standard AI capability plugin for Volcano Engine AI MediaKit. It is built on the MCP (Model Context Protocol) protocol and exposes cloud media capabilities such as video editing, audio/video processing, subtitle processing, and video enhancement as tools that can be called by AI agents. With MediaKit MCP, developers can use natural language to drive intelligent media production workflows. + +| Field | Value | +| --- | --- | +| Version | v1.0.0 | +| Description | MediaKit MCP intelligent media assistant | +| Categories | Media cloud, audio/video editing, video enhancement | +| Tags | MCP, MediaKit, video editing, audio processing, video enhancement | + +## Tool Overview + +MediaKit MCP provides tools that cover the full workflow from asynchronous task query to deep media editing and video enhancement. Tools are grouped by domain and can be loaded dynamically by group or by tool name. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CategoryGroupToolDescription
Sharedsharedquery_taskTask query: Query asynchronous task status and results after submitting an asynchronous MediaKit task. For detailed input and output parameters, see + query_task. +
Video editingeditingadd_image_to_videoAdd image to video: Add an image overlay or watermark to a video. For detailed input and output parameters, see + add_image_to_video. +
add_subtitle_to_videoAdd subtitles to video: Burn subtitle files or subtitle text into a video. For detailed input and output parameters, see + add_subtitle_to_video. +
adjust_video_speedAdjust video speed: Adjust video playback speed for fast-motion or slow-motion effects. For detailed input and output parameters, see + adjust_video_speed. +
concat_audioConcatenate audio: Concatenate multiple audio clips into a single audio file. For detailed input and output parameters, see + concat_audio. +
concat_videoConcatenate video: Concatenate multiple video clips and optionally apply transition effects. For detailed input and output parameters, see + concat_video. +
extract_audioExtract audio: Extract the audio stream from a video as an independent audio file. For detailed input and output parameters, see + extract_audio. +
flip_videoFlip video: Flip a video vertically or horizontally. For detailed input and output parameters, see + flip_video. +
image_to_videoImage to video: Generate an animated video from multiple images with optional transitions. For detailed input and output parameters, see + image_to_video. +
mux_audio_videoMux audio and video: Combine an audio track and a video track into one video file. For detailed input and output parameters, see + mux_audio_video. +
trim_audioTrim audio: Trim an audio file by start and end time. For detailed input and output parameters, see + trim_audio. +
trim_videoTrim video: Trim a video by start and end time. For detailed input and output parameters, see + trim_video. +
Video enhancementvideoerase_video_subtitle_proErase video subtitles: Remove subtitles or text from videos with high-quality restoration. For detailed input and output parameters, see + erase_video_subtitle_pro. +
enhance_videoEnhance video: Improve video quality for AIGC, UGC, short drama, education, game, and old film restoration scenarios. For detailed input and output parameters, see + enhance_video. +
+ +## Configuration + +| Variable | Description | Required | +| --- | --- | --- | +| `MEDIAKIT_API_KEY` | MediaKit API key used for authentication. | Yes | +| `MEDIAKIT_ENDPOINT` | MediaKit API endpoint. Defaults to `https://amk.cn-beijing.volces.com`. | No | +| `MCP_DOMAINS` | Comma-separated domain allowlist, such as `editing,video`. | No | +| `MCP_TOOLS` | Comma-separated tool allowlist, such as `trim_video,query_task`. | No | + +## Quick Start + +Install the package: + +```bash +pip install mcp-server-mediakit +``` + +Set your MediaKit API key: + +```bash +export MEDIAKIT_API_KEY="your-api-key" +``` + +Run with stdio transport: + +```bash +mcp-server-mediakit --transport stdio +``` + +Run with streamable HTTP transport: + +```bash +mcp-server-mediakit --transport streamable-http +``` + +## Usage Notes + +- Synchronous tasks return results directly. +- Asynchronous tasks return a `task_id`; call `query_task` to get task status and results. +- Idempotency is enabled by default. Requests from the same account with the same core parameters within 2 days return the first task result instead of creating duplicate tasks. +- To control idempotency explicitly, pass `client_token`. Reuse the same value for retries and use a new unique value to force a new task. + +## Tool Details + +### query_task + +Query asynchronous task status. Supports one-shot query or polling through `poll_interval_seconds` and `max_poll_attempts`. + +### add_image_to_video + +Add an image overlay to a video. Commonly used for image watermarks. Supports image size, position, start time, and end time. + +### add_subtitle_to_video + +Burn subtitle files or structured subtitle text into a video. Supports subtitle position, font size, font color, and font type. + +### adjust_video_speed + +Adjust video playback speed. Supports speed values from `0.1` to `4`. + +### concat_audio + +Concatenate audio clips. Supports up to 100 audio URLs. + +### concat_video + +Concatenate video clips. Supports up to 100 video URLs and optional transition effects. + +### extract_audio + +Extract audio from a video and output `mp3` or `m4a`. + +### flip_video + +Flip a video vertically or horizontally. + +### image_to_video + +Generate an animated video from multiple images and optional transition effects. + +### mux_audio_video + +Combine a video and an audio file. Supports preserving the original video audio and synchronizing audio/video duration. + +### trim_audio + +Trim an audio file by start and end time in seconds. + +### trim_video + +Trim a video by start and end time in seconds. + +### erase_video_subtitle_pro + +Remove subtitles or text from videos and restore the visual content. Supports mainstream video formats such as `mp4`, `flv`, `ts`, `avi`, `mov`, `wmv`, and `mkv`. + +### enhance_video + +Enhance video quality for scenarios such as `common`, `ugc`, `short_series`, `aigc`, and `old_film`. Supports standard and professional tool versions. diff --git a/server/mcp_server_mediakit/README_zh.md b/server/mcp_server_mediakit/README_zh.md new file mode 100644 index 00000000..8d936cbf --- /dev/null +++ b/server/mcp_server_mediakit/README_zh.md @@ -0,0 +1,218 @@ +# MediaKit MCP Server + +MediaKit MCP 是火山引擎 AI MediaKit 面向 AI 时代推出的标准能力插件。它基于 MCP(Model Context Protocol)协议,将云端专业的视频剪辑、音频处理、字幕处理、画质增强等原子能力封装为智能体可直观调用的工具。通过 MediaKit MCP,开发者可直接以自然语言驱动 AI 智能体完成复杂的云端媒体处理任务。 + +| 字段 | 取值 | +| --- | --- | +| 版本 | v1.0.0 | +| 描述 | MediaKit MCP 智能媒体助手 | +| 分类 | 视频云、音视频编辑、画质增强 | +| 标签 | MCP、MediaKit、视频剪辑、音频处理、画质增强 | + +## 工具概览 + +MediaKit MCP 已开放的能力覆盖了从异步任务查询到深度媒体编辑、视频增强的全流程。所有工具均支持通过“分组(Group)”或“工具名”进行动态加载,以优化智能体的推理效率。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
分类分组名称工具说明
通用能力sharedquery_task任务查询:查询异步任务状态和结果。提交异步任务后,使用该工具获取处理进度和最终产物。详细输入和输出参数请见 + query_task。 +
视频剪辑editingadd_image_to_video视频加图片:在视频画面上叠加图片,常用于添加图片水印。详细输入和输出参数请见 + add_image_to_video。 +
add_subtitle_to_video视频加字幕:将字幕文件或字幕文本压制到视频画面中。详细输入和输出参数请见 + add_subtitle_to_video。 +
adjust_video_speed视频播放调速:调整视频播放速度,实现快放或慢放效果。详细输入和输出参数请见 + adjust_video_speed。 +
concat_audio音频拼接:将多个音频片段拼接为一个新的音频文件。详细输入和输出参数请见 + concat_audio。 +
concat_video视频拼接:将多个视频片段拼接为一个新视频,支持添加转场效果。详细输入和输出参数请见 + concat_video。 +
extract_audio音频提取:从视频文件中分离音频流,并保存为独立音频文件。详细输入和输出参数请见 + extract_audio。 +
flip_video画面翻转:对视频画面进行水平或垂直镜像翻转。详细输入和输出参数请见 + flip_video。 +
image_to_video图片合成视频:将多张图片合成为动画视频,支持转场效果。详细输入和输出参数请见 + image_to_video。 +
mux_audio_video音画合成:将视频轨道与音频轨道合成一个视频文件。详细输入和输出参数请见 + mux_audio_video。 +
trim_audio音频裁剪:按起止时间点裁剪音频,生成新的音频片段。详细输入和输出参数请见 + trim_audio。 +
trim_video视频裁剪:按起止时间点裁剪视频,生成新的视频片段。详细输入和输出参数请见 + trim_video。 +
视频增强videoerase_video_subtitle_pro视频字幕擦除:针对视频中的字幕或文本进行高质量无痕擦除。详细输入和输出参数请见 + erase_video_subtitle_pro。 +
enhance_video画质增强:面向 AIGC、UGC、短剧、教育、游戏、老片修复等场景提升视频画质。详细输入和输出参数请见 + enhance_video。 +
+ +## 配置说明 + +| 变量 | 说明 | 是否必填 | +| --- | --- | --- | +| `MEDIAKIT_API_KEY` | MediaKit API Key,用于请求鉴权。 | 是 | +| `MEDIAKIT_ENDPOINT` | MediaKit API Endpoint,默认 `https://amk.cn-beijing.volces.com`。 | 否 | +| `MCP_DOMAINS` | 工具分组白名单,多个分组用英文逗号分隔,例如 `editing,video`。 | 否 | +| `MCP_TOOLS` | 工具名白名单,多个工具用英文逗号分隔,例如 `trim_video,query_task`。 | 否 | + +## 快速开始 + +安装 MCP Server: + +```bash +pip install mcp-server-mediakit +``` + +配置 MediaKit API Key: + +```bash +export MEDIAKIT_API_KEY="your-api-key" +``` + +以 stdio 模式启动: + +```bash +mcp-server-mediakit --transport stdio +``` + +以 Streamable HTTP 模式启动: + +```bash +mcp-server-mediakit --transport streamable-http +``` + +## 使用说明 + +- 同步任务会直接返回结果。 +- 异步任务会返回 `task_id`,需要调用 `query_task` 查询任务状态和结果。 +- 默认开启幂等:相同账户和核心请求参数在 2 天内重复提交时,服务会直接返回首次任务结果,不会重复创建任务。 +- 如需主动控制幂等,可传 `client_token`;请求重试时复用同一值,强制重新执行时必须传新的唯一值。 +- `client_token` 由客户端生成,长度不超过 64 个字符。 + +## 工具详情 + +### query_task + +查询异步任务状态。支持单次查询,也支持通过 `poll_interval_seconds` 与 `max_poll_attempts` 控制轮询。 + +### add_image_to_video + +在视频中添加图片覆盖层,可用于图片水印。支持配置图片宽高、水平位置、垂直位置、开始时间和结束时间。 + +### add_subtitle_to_video + +将字幕文件或字幕文本压制到视频画面中。支持字幕位置、字体大小、字体颜色和字体类型配置。 + +### adjust_video_speed + +调整视频播放速度,实现快放或慢放效果。支持 `0.1` 到 `4` 倍速。 + +### concat_audio + +拼接多个音频片段,最多支持 100 个音频 URL。 + +### concat_video + +拼接多个视频片段,最多支持 100 个视频 URL,并可配置转场效果。 + +### extract_audio + +从视频中提取音频,支持输出 `mp3` 或 `m4a`。 + +### flip_video + +对视频画面进行水平或垂直镜像翻转。 + +### image_to_video + +将多张图片合成为动画视频,并支持配置转场效果。 + +### mux_audio_video + +将视频与音频合成为一个视频文件。支持保留原视频音频,并支持按视频或音频基准进行时长对齐。 + +### trim_audio + +按秒级起止时间裁剪音频文件。 + +### trim_video + +按秒级起止时间裁剪视频文件。 + +### erase_video_subtitle_pro + +针对视频中的字幕或文本进行高质量擦除,尽可能还原视频画面。支持 `mp4`、`flv`、`ts`、`avi`、`mov`、`wmv`、`mkv` 等主流视频格式。 + +### enhance_video + +针对 `common`、`ugc`、`short_series`、`aigc`、`old_film` 等场景进行画质增强,支持标准版和专业版工具版本。 diff --git a/server/mcp_server_mediakit/pyproject.toml b/server/mcp_server_mediakit/pyproject.toml new file mode 100644 index 00000000..62163d0b --- /dev/null +++ b/server/mcp_server_mediakit/pyproject.toml @@ -0,0 +1,27 @@ +[build-system] +requires = ["setuptools>=68", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "mcp-server-mediakit" +version = "1.0.0" +description = "MediaKit MCP Server" +readme = "README.md" +requires-python = ">=3.11" +dependencies = [ + "mcp>=1.12.0", + "pydantic>=2.0.0", + "httpx>=0.25.0", + "pyyaml>=6.0", + "python-dotenv>=1.0.0", + "retry>=0.9.2", +] + +[project.scripts] +mcp-server-mediakit = "mediakit.server:main" + +[tool.setuptools] +package-dir = {"" = "src"} + +[tool.setuptools.packages.find] +where = ["src"] diff --git a/server/mcp_server_mediakit/src/base/__init__.py b/server/mcp_server_mediakit/src/base/__init__.py new file mode 100644 index 00000000..b4da5474 --- /dev/null +++ b/server/mcp_server_mediakit/src/base/__init__.py @@ -0,0 +1,28 @@ +from .base_mcp import BaseMCP +from .client import MediKitClient, create_client +from .constant import ( + MEDIAKIT_API_KEY_ENV, + MEDIAKIT_API_KEY_HEADER, + DEFAULT_ENDPOINT, + ENDPOINT_ENV, + ENDPOINT_HEADER, + MCP_DOMAINS_ENV, + MCP_DOMAINS_HEADER, + MCP_TOOLS_ENV, + MCP_TOOLS_HEADER, +) + +__all__ = [ + "BaseMCP", + "MediKitClient", + "create_client", + "MEDIAKIT_API_KEY_ENV", + "MEDIAKIT_API_KEY_HEADER", + "DEFAULT_ENDPOINT", + "ENDPOINT_ENV", + "ENDPOINT_HEADER", + "MCP_DOMAINS_ENV", + "MCP_DOMAINS_HEADER", + "MCP_TOOLS_ENV", + "MCP_TOOLS_HEADER", +] diff --git a/server/mcp_server_mediakit/src/base/api_info.py b/server/mcp_server_mediakit/src/base/api_info.py new file mode 100644 index 00000000..d588388d --- /dev/null +++ b/server/mcp_server_mediakit/src/base/api_info.py @@ -0,0 +1,16 @@ +api_info = { + "add_image_to_video": {"path": "/api/v1/tools/add-image-to-video", "method": "POST"}, + "add_subtitle_to_video": {"path": "/api/v1/tools/add-subtitle-to-video", "method": "POST"}, + "adjust_video_speed": {"path": "/api/v1/tools/adjust-video-speed", "method": "POST"}, + "concat_audio": {"path": "/api/v1/tools/concat-audio", "method": "POST"}, + "concat_video": {"path": "/api/v1/tools/concat-video", "method": "POST"}, + "enhance_video": {"path": "/api/v1/tools/enhance-video", "method": "POST"}, + "erase_video_subtitle_pro": {"path": "/api/v1/tools/erase-video-subtitle-pro", "method": "POST"}, + "extract_audio": {"path": "/api/v1/tools/extract-audio", "method": "POST"}, + "flip_video": {"path": "/api/v1/tools/flip-video", "method": "POST"}, + "image_to_video": {"path": "/api/v1/tools/image-to-video", "method": "POST"}, + "mux_audio_video": {"path": "/api/v1/tools/mux-audio-video", "method": "POST"}, + "query_task": {"path": "/api/v1/tasks/{task_id}", "method": "GET"}, + "trim_audio": {"path": "/api/v1/tools/trim-audio", "method": "POST"}, + "trim_video": {"path": "/api/v1/tools/trim-video", "method": "POST"}, +} diff --git a/server/mcp_server_mediakit/src/base/base_mcp.py b/server/mcp_server_mediakit/src/base/base_mcp.py new file mode 100644 index 00000000..0a2d6006 --- /dev/null +++ b/server/mcp_server_mediakit/src/base/base_mcp.py @@ -0,0 +1,206 @@ +from __future__ import annotations + +import logging +from collections.abc import Sequence +from os import environ +from typing import Any + +from mcp.server.fastmcp import FastMCP +from mcp.server.fastmcp.server import Context +from mcp.server.session import ServerSession +from mcp.types import ContentBlock +from starlette.requests import Request + +from .constant import ( + MCP_DOMAINS_HEADER, + MCP_TOOLS_HEADER, + MCP_DOMAINS_ENV, + MCP_TOOLS_ENV, +) + +logger = logging.getLogger(__name__) + + +class BaseMCP(FastMCP): + """MediaKit MCP Server 基类 + + 继承 FastMCP,提供: + 1. 工具分组过滤(list_tools 时按 domain/tool 过滤) + 2. 请求上下文获取(get_request_ctx) + 3. 在 call_tool 时将 client 实例注入 _base_mcp_store,供其动态获取当前请求凭证 + """ + + def __init__( + self, + *, + name: str | None = None, + instructions: str | None = None, + host: str = "0.0.0.0", + port: int = 8000, + streamable_http_path: str = "/mcp", + stateless_http: bool = True, + ): + super().__init__( + name=name, + instructions=instructions, + host=host, + port=port, + streamable_http_path=streamable_http_path, + stateless_http=stateless_http, + ) + self._base_mcp_store: dict[str, Any] = {} + # domain -> set(tool_name) 的映射,由 register_categories 注册时填充 + self._domain_tools_map: dict[str, set[str]] = {} + + # ─── base_mcp_store 操作 ─────────────────────────── + + def set_base_mcp_store(self, store: dict[str, Any]) -> dict[str, Any]: + """合并设置 base_mcp_store""" + self._base_mcp_store = {**self._base_mcp_store, **store} + return self._base_mcp_store + + def get_base_mcp_store(self, key: str) -> Any: + """获取 base_mcp_store 中的值""" + if key is None: + return None + return self._base_mcp_store.get(key) + + # ─── domain_tools_map 操作 ───────────────────────── + + def register_domain_tools(self, domain: str, tool_names: list[str]) -> None: + """注册 domain 及其对应的 tool 列表""" + if domain not in self._domain_tools_map: + self._domain_tools_map[domain] = set() + self._domain_tools_map[domain].update(tool_names) + logger.debug(f"Registered domain '{domain}' with tools: {tool_names}") + + def get_tool_domain(self, tool_name: str) -> str: + """根据 tool_name 查找所属 domain""" + for domain, tools in self._domain_tools_map.items(): + if tool_name in tools: + return domain + return "" + + # ─── 请求上下文 ──────────────────────────────────── + + def get_request_ctx(self) -> Request | None: + """获取当前 HTTP 请求的 Request 对象(Streamable HTTP 模式下) + + stdio 模式下返回 None。 + """ + try: + ctx: Context[ServerSession, object] | None = self.get_context() + if ctx and hasattr(ctx, "request_context") and ctx.request_context: + raw_request: Request | None = ctx.request_context.request + return raw_request + except Exception as e: + logger.debug(f"Failed to get request context: {e}") + return None + + # ─── 工具过滤配置解析 ────────────────────────────── + + def _resolve_filter_config(self) -> tuple[set[str], set[str]]: + """解析 domain / tool 过滤配置 + + 优先级:HTTP Header > 环境变量 > 无限制 + + Returns: + (allowed_domains, allowed_tools) — 空集合表示无限制 + """ + domains: set[str] = set() + tools: set[str] = set() + + # 优先级1: HTTP Header(Streamable HTTP 模式) + try: + raw_request = self.get_request_ctx() + if raw_request: + headers = raw_request.headers + domains_h = headers.get(MCP_DOMAINS_HEADER, "") + tools_h = headers.get(MCP_TOOLS_HEADER, "") + if domains_h: + domains = {item.strip() for item in domains_h.split(",") if item.strip()} + if tools_h: + tools = {item.strip() for item in tools_h.split(",") if item.strip()} + if domains or tools: + logger.debug(f"Filter from header: domains={domains}, tools={tools}") + return (domains, tools) + except Exception as e: + logger.debug(f"Failed to get filter from header: {e}") + + # 优先级2: 环境变量(Studio stdio 模式) + domains_e = environ.get(MCP_DOMAINS_ENV, "") + tools_e = environ.get(MCP_TOOLS_ENV, "") + if domains_e: + domains = {item.strip() for item in domains_e.split(",") if item.strip()} + if tools_e: + tools = {item.strip() for item in tools_e.split(",") if item.strip()} + if domains or tools: + logger.debug(f"Filter from env: domains={domains}, tools={tools}") + return (domains, tools) + + # 无限制 + logger.debug("No filter configured, all tools allowed") + return (domains, tools) + + # ─── list_tools 过滤 ─────────────────────────────── + + async def list_tools(self): + """获取可用工具列表,应用 domain / tool 过滤""" + try: + res = await super().list_tools() + allowed_domains, allowed_tools = self._resolve_filter_config() + + # 无限制 + if not allowed_domains and not allowed_tools: + return res + + filtered = [] + for tool in res: + # 通过 _domain_tools_map 查找 tool 所属 domain + tool_domain = self.get_tool_domain(tool.name) + # shared 分组工具始终加载,不受过滤规则影响 + if tool_domain == "shared": + filtered.append(tool) + continue + if allowed_domains and tool_domain not in allowed_domains: + continue + if allowed_tools and tool.name not in allowed_tools: + continue + filtered.append(tool) + + logger.info( + f"BaseMCP.list_tools: filtered to {len(filtered)}/{len(res)} tools" + ) + return filtered + except Exception as e: + logger.error(f"BaseMCP.list_tools failed: {e}") + import traceback + + logger.error(traceback.format_exc()) + return [] + + # ─── call_tool ───────────────────────────────────── + + async def call_tool( + self, + name: str, + arguments: dict[str, Any], + ) -> Sequence[ContentBlock] | dict[str, Any]: + """Call a tool by name with arguments.""" + try: + # 将当前 MCP 上下文注入 arguments,供 tool handler 使用 + ctx: Context[ServerSession, object] | None = self.get_context() + arguments["ctx"] = ctx + + # 如果 store 中有 apiRequestInstance(client 实例),设置当前 tool name 到 header + api_request_instance = self._base_mcp_store.get("apiRequestInstance") + if api_request_instance and hasattr(api_request_instance, "set_headers") and name: + api_request_instance.set_headers("x-tt-tools-name", name) + + return await super().call_tool(name, arguments) + except Exception as e: + logger.error(f"BaseMCP.call_tool failed with error: {e}") + import traceback + + logger.error(traceback.format_exc()) + raise e diff --git a/server/mcp_server_mediakit/src/base/client.py b/server/mcp_server_mediakit/src/base/client.py new file mode 100644 index 00000000..9b4aab2c --- /dev/null +++ b/server/mcp_server_mediakit/src/base/client.py @@ -0,0 +1,360 @@ +"""MediaKit API 请求客户端 + +封装 HTTP 请求能力,支持 GET / POST / PUT / DELETE / PATCH 等多种请求方式。 +自动注入鉴权 Header(x-amk-api-key / x-mediakit-endpoint), +鉴权参数获取优先级:MCP Context > 实例变量 > 环境变量 > 默认值。 + +同时提供基于 api_info 路由表的高层调用接口 call(api_name, **kwargs)。 + +Streamable HTTP / SSE 模式下的鉴权: + - client 持有 _mcp_instance 引用(指向 BaseMCP) + - 每次 HTTP 请求前调用 update_auth_from_mcp() 从当前 MCP context 动态获取 headers + - 更新到实例变量 _api_key / _endpoint,避免修改全局环境变量 +""" + +from __future__ import annotations + +import logging +import os +from typing import Any, Optional + +import httpx + +from .api_info import api_info +from .constant import ( + MEDIAKIT_API_KEY_ENV, + MEDIAKIT_API_KEY_HEADER, + DEFAULT_ENDPOINT, + DEFAULT_RUNTIME, + DEFAULT_SURFACE, + ENDPOINT_ENV, + ENDPOINT_HEADER, + RUNTIME_ENV, + RUNTIME_HEADER, +) + +try: + from mcp.server.fastmcp.server import Context + from mcp.server.session import ServerSession +except Exception: # pragma: no cover + class Context: # type: ignore + pass + + class ServerSession: # type: ignore + pass + +logger = logging.getLogger(__name__) + + +class MediKitClient: + """MediaKit API 请求客户端 + + 提供与 MediaKit API 交互的 HTTP 能力: + 1. 底层:get / post / put / delete / patch / request + 2. 高层:call(api_name, **kwargs) 基于 api_info 路由表自动路由 + + 鉴权参数(MEDIAKIT_API_KEY / MEDIAKIT_ENDPOINT)通过以下优先级获取: + 1. MCP Context Header(Streamable HTTP 模式,通过 _mcp_instance.get_context() 获取) + 2. 实例变量(_api_key / _endpoint,由 update_auth_from_mcp() 更新) + 3. 环境变量(Studio stdio 模式) + 4. 默认值(仅 MEDIAKIT_ENDPOINT,默认 DEFAULT_ENDPOINT) + """ + + DEFAULT_TIMEOUT = 30.0 + + def __init__(self, timeout: float | None = None) -> None: + self._timeout = timeout or self.DEFAULT_TIMEOUT + self._mcp_instance: Any = None + self._extra_headers: dict[str, str] = {} + self._api_key: str | None = None + self._endpoint: str | None = None + self._runtime: str | None = None + + # ─── MCP 实例绑定 ────────────────────────────────── + + def set_mcp_instance(self, mcp_instance: Any) -> None: + """绑定 BaseMCP 实例,用于动态获取当前请求上下文""" + self._mcp_instance = mcp_instance + + def set_headers(self, key: str, value: str) -> None: + """设置额外 header(如 x-tt-tools-name)""" + self._extra_headers[key] = value + + # ─── 从 MCP Context 更新凭证 ─────────────────────── + + def update_auth_from_mcp(self) -> None: + """从 MCP context 动态更新凭证(Streamable HTTP 模式) + + 每次发起 HTTP 请求前调用,将当前请求的 headers 更新到实例变量。 + """ + if self._mcp_instance is None: + logger.debug("update_auth_from_mcp: _mcp_instance is None, skipping") + return + + try: + ctx = self._mcp_instance.get_context() + if ctx is None: + logger.debug("update_auth_from_mcp: get_context() returned None") + return + + self._update_auth_from_context(ctx) + except Exception as e: + logger.debug(f"update_auth_from_mcp: failed to get context: {e}") + + def _update_auth_from_context( + self, ctx: Optional[Context[ServerSession, object, Any]] = None + ) -> None: + """从 MCP context 的 headers 中提取凭证并更新到实例变量""" + if ctx is None: + return + + try: + raw_request = ctx.request_context.request + if not raw_request: + return + + headers = raw_request.headers + if not headers: + return + + api_key = headers.get(MEDIAKIT_API_KEY_HEADER) + endpoint = headers.get(ENDPOINT_HEADER) + runtime = headers.get(RUNTIME_HEADER) + + if api_key: + self._api_key = api_key + logger.debug("Credentials updated from MCP context: api_key") + if endpoint: + self._endpoint = endpoint + logger.debug("Credentials updated from MCP context: endpoint") + if runtime: + self._runtime = runtime + except Exception as e: + logger.warning(f"Failed to update credentials from context: {e}") + + # ─── 鉴权参数解析 ─────────────────────────────────── + + def _resolve_api_key(self) -> str: + """解析 MEDIAKIT_API_KEY + + 优先级:实例变量 > 环境变量 + 未找到时抛出 ValueError。 + """ + if self._api_key: + return self._api_key + env_val = os.environ.get(MEDIAKIT_API_KEY_ENV, "") + if not env_val: + raise ValueError( + f"缺少 API Key,请通过环境变量 {MEDIAKIT_API_KEY_ENV} " + f"或 Header {MEDIAKIT_API_KEY_HEADER} 设置" + ) + return env_val + + def _resolve_endpoint(self) -> str: + """解析 MEDIAKIT_ENDPOINT + + 优先级:实例变量 > 环境变量 > DEFAULT_ENDPOINT + """ + if self._endpoint: + return self._endpoint + return os.environ.get(ENDPOINT_ENV, "") or DEFAULT_ENDPOINT + + def _resolve_surface(self) -> str: + return DEFAULT_SURFACE + + def _resolve_runtime(self) -> str: + if self._runtime: + return self._runtime + return os.environ.get(RUNTIME_ENV, "") or DEFAULT_RUNTIME + + # ─── 请求 Header 构建 ────────────────────────────── + + def _build_request_headers(self) -> dict[str, str]: + """构建请求 Header,注入鉴权信息""" + api_key = self._resolve_api_key() + headers: dict[str, str] = { + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json", + "x-surface": self._resolve_surface(), + "x-runtime": self._resolve_runtime(), + } + return headers + + # ─── 响应处理 ────────────────────────────────────── + + @staticmethod + def _handle_response(response: httpx.Response) -> dict[str, Any]: + """处理 HTTP 响应,检查状态码并解析 JSON + + 非 2xx 状态码抛出 httpx.HTTPStatusError。 + """ + response.raise_for_status() + return response.json() + + # ─── 通用请求方法 ────────────────────────────────── + + def request( + self, + method: str, + path: str, + *, + headers: dict[str, str] | None = None, + params: dict[str, Any] | None = None, + json: dict[str, Any] | None = None, + data: dict[str, Any] | None = None, + ) -> dict[str, Any]: + """通用 HTTP 请求方法 + + 每次请求前自动调用 update_auth_from_mcp() 更新凭证。 + + Args: + method: HTTP 方法(GET / POST / PUT / DELETE / PATCH) + path: API 路径,如 /api/v1/tools/trim-video + headers: 额外请求 Header + params: URL 查询参数 + json: JSON 请求体 + data: Form 请求体 + + Returns: + API 响应 JSON 解析结果 + + Raises: + ValueError: 缺少 MEDIAKIT_API_KEY + httpx.HTTPStatusError: API 返回非 2xx 状态码 + """ + self.update_auth_from_mcp() + request_headers = self._build_request_headers() + endpoint = self._resolve_endpoint() + url = f"{endpoint.rstrip('/')}/{path.lstrip('/')}" + + with httpx.Client(timeout=self._timeout) as client: + response = client.request( + method=method.upper(), + url=url, + headers=request_headers, + params=params, + json=json, + data=data, + ) + return self._handle_response(response) + + # ─── 便捷方法 ────────────────────────────────────── + + def get( + self, + path: str, + *, + params: dict[str, Any] | None = None, + headers: dict[str, str] | None = None, + ) -> dict[str, Any]: + """GET 请求,参数走 query string""" + return self.request("GET", path, headers=headers, params=params) + + def post( + self, + path: str, + *, + json: dict[str, Any] | None = None, + data: dict[str, Any] | None = None, + headers: dict[str, str] | None = None, + ) -> dict[str, Any]: + """POST 请求,支持 JSON body 或 form data""" + return self.request("POST", path, headers=headers, json=json, data=data) + + def put( + self, + path: str, + *, + json: dict[str, Any] | None = None, + data: dict[str, Any] | None = None, + headers: dict[str, str] | None = None, + ) -> dict[str, Any]: + """PUT 请求""" + return self.request("PUT", path, headers=headers, json=json, data=data) + + def delete( + self, + path: str, + *, + params: dict[str, Any] | None = None, + headers: dict[str, str] | None = None, + ) -> dict[str, Any]: + """DELETE 请求""" + return self.request("DELETE", path, headers=headers, params=params) + + def patch( + self, + path: str, + *, + json: dict[str, Any] | None = None, + data: dict[str, Any] | None = None, + headers: dict[str, str] | None = None, + ) -> dict[str, Any]: + """PATCH 请求""" + return self.request("PATCH", path, headers=headers, json=json, data=data) + + # ─── 基于 api_info 路由表的高层调用 ──────────────── + + def call( + self, + api_name: str, + **kwargs: Any, + ) -> dict[str, Any]: + """根据 api_info 路由表调用指定 API。 + + 路径中的占位符(如 {task_id})会从 kwargs 中提取并替换, + 剩余的参数作为 query params 或 json body 传入。 + + Args: + api_name: API 名称(snake_case),如 "query_task" + **kwargs: 请求参数 + - 路径参数:匹配 path 中的 {xxx} 占位符 + - GET / DELETE:剩余参数作为 query params 传入 + - POST / PUT / PATCH:剩余参数作为 json body 传入 + + Returns: + API 响应 JSON + + Raises: + KeyError: api_info 中不存在该 API + httpx.HTTPStatusError: API 返回非 2xx 状态码 + """ + route = api_info[api_name] + method = route["method"].upper() + path = route["path"] + + # 提取路径参数并替换占位符 + path_params = {} + remaining_kwargs = dict(kwargs) + for key in list(remaining_kwargs.keys()): + placeholder = "{" + key + "}" + if placeholder in path: + path_params[key] = remaining_kwargs.pop(key) + path = path.format(**path_params) + remaining_kwargs = { + key: value for key, value in remaining_kwargs.items() if value is not None + } + + if method == "GET": + return self.get(path, params=remaining_kwargs) + if method == "POST": + return self.post(path, json=remaining_kwargs) + if method == "PUT": + return self.put(path, json=remaining_kwargs) + if method == "DELETE": + return self.delete(path, params=remaining_kwargs) + if method == "PATCH": + return self.patch(path, json=remaining_kwargs) + + return self.request(method, path, json=remaining_kwargs) + + +# ─── 工厂函数 ────────────────────────────────────── + +def create_client(timeout: float | None = None) -> MediKitClient: + """创建新的 MediKitClient 实例。 + + 每个请求使用独立 client 实例,避免并发场景下鉴权信息冲突。 + 实际并发安全由 update_auth_from_mcp() 保证(动态获取当前请求上下文)。 + """ + return MediKitClient(timeout=timeout) diff --git a/server/mcp_server_mediakit/src/base/constant.py b/server/mcp_server_mediakit/src/base/constant.py new file mode 100644 index 00000000..6f2af81b --- /dev/null +++ b/server/mcp_server_mediakit/src/base/constant.py @@ -0,0 +1,25 @@ +## 服务配置 mediakit api-key & endpoint +# 环境变量名 +MEDIAKIT_API_KEY_ENV = "MEDIAKIT_API_KEY" +ENDPOINT_ENV = "MEDIAKIT_ENDPOINT" +RUNTIME_ENV = "MEDIAKIT_RUNTIME" +DEFAULT_SURFACE = "mcp" +DEFAULT_RUNTIME = "unknown" +# 分组&按照工具名加载 +MCP_DOMAINS_ENV = "MCP_DOMAINS" +MCP_TOOLS_ENV = "MCP_TOOLS" + +# HTTP Header 名 +MEDIAKIT_API_KEY_HEADER = "x-amk-api-key" +ENDPOINT_HEADER = "x-mediakit-endpoint" +RUNTIME_HEADER = "x-runtime" + +# 分组&按照工具名加载 +MCP_DOMAINS_HEADER = "x-mcp-domains" +MCP_TOOLS_HEADER = "x-mcp-tools" + +# 默认 endpoint +DEFAULT_ENDPOINT = "https://amk.cn-beijing.volces.com" + +# 控制台入口,供缺失/错误配置时的 reference_url +AMK_CONSOLE_REFERENCE = "https://console.volcengine.com/imp/ai-mediakit/" diff --git a/server/mcp_server_mediakit/src/mediakit/mcp_server.py b/server/mcp_server_mediakit/src/mediakit/mcp_server.py new file mode 100644 index 00000000..4233b31a --- /dev/null +++ b/server/mcp_server_mediakit/src/mediakit/mcp_server.py @@ -0,0 +1,34 @@ +from __future__ import annotations + +import importlib +import logging +import pkgutil + +from base.client import MediKitClient + +logger = logging.getLogger(__name__) + + +def register_categories(mcp, client: MediKitClient) -> None: + """自动发现并注册 mcp_tools 下所有 domain 模块的 tool。 + + 遍历 mediakit.mcp_tools 包下所有模块, + 对每个含 register_tools 函数的模块调用注册,传入 mcp 和 client。 + 模块需定义 TOOL_NAMES 列表,注册后通过 mcp.register_domain_tools 维护映射。 + """ + from . import mcp_tools as tools_pkg + + modules = sorted(pkgutil.iter_modules(tools_pkg.__path__), key=lambda item: item[1]) + for _, mod_name, is_pkg in modules: + if is_pkg: + continue + try: + module = importlib.import_module(f".{mod_name}", package=tools_pkg.__package__) + if hasattr(module, "register_tools"): + module.register_tools(mcp, client) + + # 注册 domain -> tools 映射 + if hasattr(mcp, "register_domain_tools") and hasattr(module, "TOOL_NAMES"): + mcp.register_domain_tools(mod_name, module.TOOL_NAMES) + except Exception as exc: + logger.exception("Failed to register MCP tools from module %s: %s", mod_name, exc) diff --git a/server/mcp_server_mediakit/src/mediakit/mcp_tools/__init__.py b/server/mcp_server_mediakit/src/mediakit/mcp_tools/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/server/mcp_server_mediakit/src/mediakit/mcp_tools/editing.py b/server/mcp_server_mediakit/src/mediakit/mcp_tools/editing.py new file mode 100644 index 00000000..9f3be50e --- /dev/null +++ b/server/mcp_server_mediakit/src/mediakit/mcp_tools/editing.py @@ -0,0 +1,237 @@ +from __future__ import annotations + +from typing import Any, Dict, List, Optional +from typing_extensions import NotRequired, Required, TypedDict + +try: + from pydantic import Field +except Exception: # pragma: no cover + def Field(*args, **kwargs): + if args: + return args[0] + return kwargs.get("default", None) + +try: + from mcp.server.fastmcp.server import Context + from mcp.server.session import ServerSession +except Exception: # pragma: no cover + class Context: # type: ignore + pass + + class ServerSession: # type: ignore + pass + +from base.client import MediKitClient +from ..utils.response import async_task_response, error_response + +class AddSubtitleToVideoSubtitlesItem(TypedDict): + subtitle_text: Required[str] + start_time: Required[float] + end_time: Required[float] + +class ImageToVideoImagesItem(TypedDict): + image_url: Required[str] + duration: NotRequired[float] + animation_type: NotRequired[str] + animation_in: NotRequired[float] + animation_out: NotRequired[float] + +TOOL_NAMES = ['add_image_to_video', 'add_subtitle_to_video', 'adjust_video_speed', 'concat_audio', 'concat_video', 'extract_audio', 'flip_video', 'image_to_video', 'mux_audio_video', 'trim_audio', 'trim_video'] + + +def register_tools(mcp, client: MediKitClient) -> None: + @mcp.tool(name="add_image_to_video", description="视频加图片,可用作加图片水印。 使用 task_id, 调用 query_task 方法获取结果") + async def add_image_to_video( + video_url: str = Field(..., description="输入视频。String 类型,支持http://xxx或https://xxx格式 URL"), + sub_image_url: str = Field(..., description="图片URL。支持http://xxx或https://xxx格式 URL"), + sub_image_height: Optional[str] = Field('5%', description="图片的高度,字符串类型,支持具体像素值(如 '100')或百分比(如 '20%',相对于视频高度)。"), + sub_image_width: Optional[str] = Field('10%', description="图片的宽度,字符串类型,支持具体像素值(如 '100')或百分比(如 '20%',相对于视频高度)。"), + sub_image_pos_x: Optional[str] = Field('85%', description="图片在水平方向(X 轴)的位置,以视频左上角为原点,字符串类型,支持具体像素值(如 '100')或百分比(如 '20%')。例如值为 '0' 时,表示处于最左侧。"), + sub_image_pos_y: Optional[str] = Field('90%', description="图片在垂直方向(Y 轴)的位置,以视频左上角为原点,字符串类型,支持具体像素值(如 '100')或百分比(如 '20%')。例如值为 '0' 时,表示处于最上侧。"), + start_time: Optional[float] = Field(None, description="图片的开始时间,单位:秒。不传默认同视频开始时间"), + end_time: Optional[float] = Field(None, description="图片的结束时间,单位:秒。注意:如果设置的开始/结束时间超出原始视频时长,输出视频长度将以该结束时间为准,超出部分以黑屏形式延续。不传默认同视频结束时间"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """视频加图片,可用作加图片水印。""" + try: + result = client.call(api_name="add_image_to_video", video_url=video_url, sub_image_url=sub_image_url, sub_image_height=sub_image_height, sub_image_width=sub_image_width, sub_image_pos_x=sub_image_pos_x, sub_image_pos_y=sub_image_pos_y, start_time=start_time, end_time=end_time, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="add_subtitle_to_video", description="将字幕文件或文本内容,以指定样式压制到视频画面中,生成带内嵌字幕的新视频。 使用 task_id, 调用 query_task 方法获取结果") + async def add_subtitle_to_video( + video_url: str = Field(..., description="输入视频。String 类型,支持http://xxx或https://xxx格式 URL"), + subtitle_url: Optional[str] = Field(None, description="字幕文件 URL、filename。常见的字幕文件为 SRT、VTT、ASS 等格式。"), + subtitles: Optional[List[AddSubtitleToVideoSubtitlesItem]] = Field(None, description="字幕列表,Array类型。"), + subtitle_pos_preset: Optional[str] = Field('bottom_center', description="预设字幕位置。底部居中(默认常用) bottom_center;顶部居中 top_center;画面正中央 center;偏下三分之一处 lower_third"), + subtitle_font_size: Optional[int] = Field(50, description="字幕的字体大小,单位:像素。"), + subtitle_font_color: Optional[str] = Field('#FFFFFFFF', description="字幕的字体颜色,RGBA 格式。默认#FFFFFFFF"), + subtitle_font_type: Optional[str] = Field('sy_black', description="字幕的字体 ID。 思源黑体:sy_black (经典无衬线黑体,端正百搭,正文首选) 庞门正道标题体:pm_zhengdao (粗壮有力,硬汉气场,大标题/封面神器) 阿里巴巴普惠体:ali_puhui (现代感极强,结构饱满,屏幕阅读体验极佳) 站酷快乐体:zhanku_kuaile (圆润活泼,带手写感,适合轻松搞笑的 Vlog 氛围)"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """将字幕文件或文本内容,以指定样式压制到视频画面中,生成带内嵌字幕的新视频。""" + try: + result = client.call(api_name="add_subtitle_to_video", video_url=video_url, subtitle_url=subtitle_url, subtitles=subtitles, subtitle_pos_preset=subtitle_pos_preset, subtitle_font_size=subtitle_font_size, subtitle_font_color=subtitle_font_color, subtitle_font_type=subtitle_font_type, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="adjust_video_speed", description="调整视频的播放倍速,实现快放或慢放效果。 使用 task_id, 调用 query_task 方法获取结果") + async def adjust_video_speed( + video_url: str = Field(..., description="输入视频。String 类型,支持http://xxx或https://xxx格式 URL"), + speed: Optional[float] = Field(1, description="调整速度的倍数,Float类型,取值范围为0.1~4。"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """调整视频的播放倍速,实现快放或慢放效果。""" + try: + result = client.call(api_name="adjust_video_speed", video_url=video_url, speed=speed, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="concat_audio", description="拼接多个音频片段。 使用 task_id, 调用 query_task 方法获取结果") + async def concat_audio( + audio_urls: List[str] = Field(..., description="待拼接的音频列表,Array类型。最少传入1个,最多传入100个\n子项说明:待拼接的输入音频。String 类型,支持http://xxx或https://xxx格式 URL"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """拼接多个音频片段。""" + try: + result = client.call(api_name="concat_audio", audio_urls=audio_urls, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="concat_video", description="拼接多个视频片段,支持添加转场效果。 使用 task_id, 调用 query_task 方法获取结果") + async def concat_video( + video_urls: List[str] = Field(..., description="待拼接的视频列表,Array类型。最少传入1个,最多传入100个\n子项说明:待拼接的输入视频。String 类型,支持http://xxx或https://xxx格式 URL"), + transitions: Optional[List[str]] = Field(None, description="转场效果 ID,Array 类型。如果不提供,则没有转场。当视频数量超过转场数量 2 个及以上时,系统将自动循环使用转场。例如有 10 个视频,2 种转场效果,那么在 9 处拼接点上,这 2 种转场效果将被依次循环使用。\n子项说明:转场效果 ID\n分类:交替出场,ID:1182359\n分类:旋转放大,ID:1182360\n分类:泛开,ID:1182358\n分类:六角形,ID:1182365\n分类:故障转换,ID:1182367\n分类:飞眼,ID:1182368\n分类:梦幻放大,ID:1182369\n分类:开门展现,ID:1182370\n分类:立方转换,ID:1182373\n分类:透镜变换,ID:1182374\n分类:晚霞转场,ID:1182375\n分类:圆形交替,ID:1182378"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """拼接多个视频片段,支持添加转场效果。""" + try: + result = client.call(api_name="concat_video", video_urls=video_urls, transitions=transitions, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="extract_audio", description="将视频文件中的音频流分离并保存为独立的音频文件。 使用 task_id, 调用 query_task 方法获取结果") + async def extract_audio( + video_url: str = Field(..., description="输入视频,String 类型,支持http://xxx或https://xxx格式 URL"), + format: Optional[str] = Field('m4a', description="输出音频的格式,支持 mp3、m4a 格式。 默认m4a"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """将视频文件中的音频流分离并保存为独立的音频文件。""" + try: + result = client.call(api_name="extract_audio", video_url=video_url, format=format, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="flip_video", description="对视频画面进行上下或左右镜像翻转。 使用 task_id, 调用 query_task 方法获取结果") + async def flip_video( + video_url: str = Field(..., description="输入视频。String 类型,支持http://xxx或https://xxx格式 URL"), + is_flip_vertical: Optional[bool] = Field(False, description="是否进行垂直翻转。Boolean 类型,默认值为 false, 表示不翻转。"), + is_flip_horizontal: Optional[bool] = Field(False, description="是否进行水平翻转。Boolean 类型,默认值为 false, 表示不翻转。"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """对视频画面进行上下或左右镜像翻转。""" + try: + result = client.call(api_name="flip_video", video_url=video_url, is_flip_vertical=is_flip_vertical, is_flip_horizontal=is_flip_horizontal, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="image_to_video", description="多张图片生成动画视频。 使用 task_id, 调用 query_task 方法获取结果") + async def image_to_video( + images: List[ImageToVideoImagesItem] = Field(..., description="待合成的图片列表,Array类型。最少传入1个,最多传入100个\n子项说明:待合成的图片。Image类型"), + transitions: Optional[List[str]] = Field(None, description="转场效果 ID,Array 类型。如果不提供,则没有转场。当视频数量超过转场数量 2 个及以上时,系统将自动循环使用转场。例如有 10 个视频,2 种转场效果,那么在 9 处拼接点上,这 2 种转场效果将被依次循环使用。\n子项说明:转场效果 ID\n分类:交替出场,ID:1182359\n分类:旋转放大,ID:1182360\n分类:泛开,ID:1182358\n分类:六角形,ID:1182365\n分类:故障转换,ID:1182367\n分类:飞眼,ID:1182368\n分类:梦幻放大,ID:1182369\n分类:开门展现,ID:1182370\n分类:立方转换,ID:1182373\n分类:透镜变换,ID:1182374\n分类:晚霞转场,ID:1182375\n分类:圆形交替,ID:1182378"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """多张图片生成动画视频。""" + try: + result = client.call(api_name="image_to_video", images=images, transitions=transitions, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="mux_audio_video", description="音视频合成。 使用 task_id, 调用 query_task 方法获取结果") + async def mux_audio_video( + video_url: str = Field(..., description="输入视频。String 类型,支持http://xxx或https://xxx格式 URL"), + audio_url: str = Field(..., description="输入音频。String 类型,支持http://xxx或https://xxx格式 URL"), + is_audio_reserve: Optional[bool] = Field(True, description="Boolean 类型,是否保留原视频流中的音频。默认值 true:保留。false:不保留。"), + is_video_audio_sync: Optional[bool] = Field(False, description="Boolean 类型,是否对齐音频和视频时长。 true:通过 output_sync 配置,对齐音频和视频时长。 false(默认值):保持原样输出,不做音视频对齐。最终合成的视频时长,以较长的流为准。"), + sync_mode: Optional[str] = Field('video', description="String 类型,设置 is_video_audio_sync 为 true 时生效;当音频和视频时长不相等时,可指定对齐基准,可选项:video、audio。 video:【默认值】以视频的时长为准。 audio:以音频的时长为准。"), + sync_method: Optional[str] = Field('trim', description="String 类型,设置 is_video_audio_sync 为 true 时生效;指定对齐方式,支持通过裁剪或加速的方式,对齐音频和视频的时长。可选项:speed、trim。 speed:通过加快音频或视频的速度,对齐音频和视频的时长。 trim:【默认值】通过裁剪音频或视频,对齐音频和视频的时长。从头开始计算并裁剪。"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """音视频合成。""" + try: + result = client.call(api_name="mux_audio_video", video_url=video_url, audio_url=audio_url, is_audio_reserve=is_audio_reserve, is_video_audio_sync=is_video_audio_sync, sync_mode=sync_mode, sync_method=sync_method, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="trim_audio", description="按起止时间点(秒级)裁剪音频,生成新片段。 使用 task_id, 调用 query_task 方法获取结果") + async def trim_audio( + audio_url: str = Field(..., description="输入纯音频。String 类型,支持http://xxx或https://xxx格式 URL"), + start_time: Optional[float] = Field(0, description="裁剪开始时间,默认为 0, 表示从头开始裁剪。支持设置为 2 位小数,单位:秒。"), + end_time: Optional[float] = Field(None, description="裁剪结束时间,默认为片源结尾。支持设置为 2 位小数,单位:秒。"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """按起止时间点(秒级)裁剪音频,生成新片段。""" + try: + result = client.call(api_name="trim_audio", audio_url=audio_url, start_time=start_time, end_time=end_time, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="trim_video", description="按起止时间点裁剪视频,生成新片段。 使用 task_id, 调用 query_task 方法获取结果") + async def trim_video( + video_url: str = Field(..., description="输入视频。String 类型,支持http://xxx或https://xxx格式 URL"), + start_time: Optional[float] = Field(0, description="裁剪开始时间,默认为 0, 表示从头开始裁剪。支持设置为 2 位小数,单位:秒。"), + end_time: Optional[float] = Field(None, description="裁剪结束时间,默认为片源结尾。支持设置为 2 位小数,单位:秒。"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """按起止时间点裁剪视频,生成新片段。""" + try: + result = client.call(api_name="trim_video", video_url=video_url, start_time=start_time, end_time=end_time, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + if hasattr(mcp, "register_domain_tools"): + mcp.register_domain_tools("editing", TOOL_NAMES) diff --git a/server/mcp_server_mediakit/src/mediakit/mcp_tools/shared.py b/server/mcp_server_mediakit/src/mediakit/mcp_tools/shared.py new file mode 100644 index 00000000..1b6fe863 --- /dev/null +++ b/server/mcp_server_mediakit/src/mediakit/mcp_tools/shared.py @@ -0,0 +1,69 @@ +from __future__ import annotations + +from typing import Optional + +try: + from pydantic import Field +except Exception: # pragma: no cover + def Field(*args, **kwargs): + if args: + return args[0] + return kwargs.get("default", None) + +try: + from mcp.server.fastmcp.server import Context + from mcp.server.session import ServerSession +except Exception: # pragma: no cover + class Context: # type: ignore + pass + + class ServerSession: # type: ignore + pass + +from base.client import MediKitClient +from ..utils.async_poller import poll_until_complete +from ..utils.response import error_response, query_task_response + + +# 本 domain 注册的 tool 名称列表 +TOOL_NAMES = ["query_task"] + + +def register_tools(mcp, client: MediKitClient) -> None: + @mcp.tool( + name="query_task", + description="查询异步任务状态。提交异步任务后使用此工具获取结果。推荐使用 poll_interval_seconds + max_poll_attempts 控制轮询,例如 poll_interval_seconds=2、max_poll_attempts=10。传递这两个参数时,不需要再传 poll_complete【不推荐】。单次调用时长 poll_interval_seconds * max_poll_attempts 建议不超过 30s,避免服务与 client 断链;若未查到终态,建议再次发起查询。", + ) + async def query_task( + task_id: str = Field(..., description="异步任务 ID,由异步能力提交后返回"), + poll_interval_seconds: Optional[float] = Field(10, description="轮询间隔(秒)。推荐与 max_poll_attempts 搭配使用,例如 2。"), + max_poll_attempts: Optional[int] = Field(0, description="最大轮询次数。0 表示仅查一次不轮询。推荐与 poll_interval_seconds 搭配使用,例如 10。"), + poll_complete: Optional[bool] = Field(False, description="是否阻塞直到任务完成。推荐使用 poll_interval_seconds + max_poll_attempts 控制轮询;传递这两个参数时通常不需要再传 poll_complete。"), + *, + ctx: Context, + ) -> dict: + """查询异步任务状态。""" + try: + def _do_query() -> dict: + return client.call(api_name="query_task", task_id=task_id) + + result = _do_query() + + # 如果配置了轮询且任务未终态,进入轮询逻辑 + status = result.get("status") + if status not in {"completed", "failed", "canceled"}: + if poll_complete or max_poll_attempts > 0: + result = await poll_until_complete( + _do_query, + poll_interval_seconds=poll_interval_seconds or 10, + max_poll_attempts=max_poll_attempts or 0, + poll_complete=poll_complete or False, + ) + + return query_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + # 注册 domain -> tools 映射 + if hasattr(mcp, "register_domain_tools"): + mcp.register_domain_tools("shared", TOOL_NAMES) diff --git a/server/mcp_server_mediakit/src/mediakit/mcp_tools/video.py b/server/mcp_server_mediakit/src/mediakit/mcp_tools/video.py new file mode 100644 index 00000000..b56213f9 --- /dev/null +++ b/server/mcp_server_mediakit/src/mediakit/mcp_tools/video.py @@ -0,0 +1,79 @@ +from __future__ import annotations + +from typing import Any, Dict, List, Optional +from typing_extensions import NotRequired, Required, TypedDict + +try: + from pydantic import Field +except Exception: # pragma: no cover + def Field(*args, **kwargs): + if args: + return args[0] + return kwargs.get("default", None) + +try: + from mcp.server.fastmcp.server import Context + from mcp.server.session import ServerSession +except Exception: # pragma: no cover + class Context: # type: ignore + pass + + class ServerSession: # type: ignore + pass + +from base.client import MediKitClient +from ..utils.response import async_task_response, error_response + +class EraseVideoSubtitleProEraseRatioLocationItem(TypedDict): + top_left_x: Required[float] + top_left_y: Required[float] + bottom_right_x: Required[float] + bottom_right_y: Required[float] + +TOOL_NAMES = ['enhance_video', 'erase_video_subtitle_pro'] + + +def register_tools(mcp, client: MediKitClient) -> None: + @mcp.tool(name="enhance_video", description="画质增强:针对 AIGC / UGC / 短剧 / 教育 / 游戏 / 老片修复等场景,提供画质提升 + 超分增强一站式解决方案。依托 AI MediaKit 智能媒体处理引擎,融合视频内容理解、画质指标智能决策、多维度增强原子算法,实现画质的全面优化。\n支持格式:主流视频格式如mp4、flv、ts、avi、mov、wmv、mkv。\n使用限制:单文件大小不超过100G。 使用 task_id, 调用 query_task 方法获取结果") + async def enhance_video( + video_url: str = Field(..., description="输入视频。String 类型,支持http://xxx或https://xxx格式 URL"), + scene: Optional[str] = Field('common', description="场景化模板类型。用于选择一个针对特定业务场景的预设画质增强模板。支持的取值如下:common(默认值): 通用模板;ugc: UGC 短视频;short_series: 短剧;aigc: AIGC 内容;old_film: 老片修复"), + tool_version: Optional[str] = Field('standard', description="工具版本,标准版:standard,专业版:professional,默认为标准版"), + resolution: Optional[str] = Field(None, description="目标分辨率。支持的取值如下所示。配置此参数后,不可同时配置resolution_limit字段"), + resolution_limit: Optional[int] = Field(None, description="目标长宽限制,用于指定输出视频的长边或短边的最大像素值,取值范围为 [64, 2160]。配置此参数后,不可同时配置resolution字段"), + fps: Optional[float] = Field(None, description="目标帧率,单位为 fps。取值范围为 (0, 120]。"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """画质增强:针对 AIGC / UGC / 短剧 / 教育 / 游戏 / 老片修复等场景,提供画质提升 + 超分增强一站式解决方案。依托 AI MediaKit 智能媒体处理引擎,融合视频内容理解、画质指标智能决策、多维度增强原子算法,实现画质的全面优化。 +支持格式:主流视频格式如mp4、flv、ts、avi、mov、wmv、mkv。 +使用限制:单文件大小不超过100G。""" + try: + result = client.call(api_name="enhance_video", video_url=video_url, scene=scene, tool_version=tool_version, resolution=resolution, resolution_limit=resolution_limit, fps=fps, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="erase_video_subtitle_pro", description="针对视频中的字幕,实现高质量的无痕擦除,最大程度的还原视频画面。\n支持格式:主流视频格式如mp4、flv、ts、avi、mov、wmv、mkv。 使用 task_id, 调用 query_task 方法获取结果") + async def erase_video_subtitle_pro( + video_url: str = Field(..., description="输入视频。String 类型,支持http://xxx或https://xxx格式 URL"), + mode: Optional[str] = Field('Subtitle', description="字幕擦除模式,取值如下:Subtitle:擦除OCR检测为字幕的文本。在此模式下,系统将启用 OCR 识别,并依据检测结果进行擦除操作,仅擦除下面50%画面的字幕。 Text:擦除OCR检测为字幕及其他的文本(如人物介绍等),不包含场景文字(如宫殿门牌匾等)。"), + output_encode_mode: Optional[str] = Field('Quality', description="输出视频编码模式,支持以下两种取值:Quality(默认值):画质优先模式。此模式下,系统会采用较高的目标码率进行编码,以确保高画质。这通常会导致输出文件的码率显著高于源文件,文件体积也相应增大。 Size:大小优先模式。在保证一定画质的前提下,使输出码率尽量向源视频码率对齐。"), + erase_ratio_location: Optional[List[EraseVideoSubtitleProEraseRatioLocationItem]] = Field(None, description="擦除框数组。添加擦除框后,系统仅擦除框内文本。\n子项说明:擦除框位置信息"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """针对视频中的字幕,实现高质量的无痕擦除,最大程度的还原视频画面。 +支持格式:主流视频格式如mp4、flv、ts、avi、mov、wmv、mkv。""" + try: + result = client.call(api_name="erase_video_subtitle_pro", video_url=video_url, mode=mode, output_encode_mode=output_encode_mode, erase_ratio_location=erase_ratio_location, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + if hasattr(mcp, "register_domain_tools"): + mcp.register_domain_tools("video", TOOL_NAMES) diff --git a/server/mcp_server_mediakit/src/mediakit/server.py b/server/mcp_server_mediakit/src/mediakit/server.py new file mode 100644 index 00000000..2bd8d711 --- /dev/null +++ b/server/mcp_server_mediakit/src/mediakit/server.py @@ -0,0 +1,62 @@ +from __future__ import annotations + +import argparse +import asyncio +import os +import sys + +from dotenv import load_dotenv + +from base.base_mcp import BaseMCP +from base.client import create_client + +from .mcp_server import register_categories + +load_dotenv() + +INSTRUCTIONS_TEXT = ( + "## MediaKit MCP is the MediaKit MCP Server\n" + "### Before using the MediaKit service, please note:\n" + "- 同步任务直接返回结果\n" + "- 异步任务返回 task_id, 使用 task_id, 调用 query_task 查询任务状态\n" + "- 默认开启幂等:相同账户 + 核心请求参数在 2 天内重复提交时,服务会直接返回首次任务结果,不会重复创建任务\n" + "- 如需主动控制幂等,可传 client_token;请求重试时复用同一值,强制重新执行时必须传新的唯一值\n" + "- client_token 由客户端生成,长度不超过 64 个字符\n" +) + +mcp = BaseMCP( + name="MediaKit MCP", + host=os.getenv("MCP_SERVER_HOST", "0.0.0.0"), + port=int(os.getenv("MCP_SERVER_PORT", "8000")), + stateless_http=os.getenv("STATLESS_HTTP", "true").lower() == "true", + streamable_http_path=os.getenv("STREAMABLE_HTTP_PATH", "/mcp"), + instructions=INSTRUCTIONS_TEXT, +) + + +def main() -> None: + try: + parser = argparse.ArgumentParser(description="Run the MediaKit MCP Server") + parser.add_argument( + "--transport", + "-t", + choices=["streamable-http", "stdio"], + default="stdio", + help="Transport protocol to use (streamable-http or stdio)", + ) + args = parser.parse_args() + + # 初始化 MCP Server:创建 client、绑定 mcp、注册 tools + client = create_client() + client.set_mcp_instance(mcp) + mcp.set_base_mcp_store({"apiRequestInstance": client}) + register_categories(mcp, client) + + asyncio.run(mcp.run(transport=args.transport)) + except Exception as e: + print(f"Error occurred while starting the server: {e}", file=sys.stderr) + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/server/mcp_server_mediakit/src/mediakit/utils/__init__.py b/server/mcp_server_mediakit/src/mediakit/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/server/mcp_server_mediakit/src/mediakit/utils/async_poller.py b/server/mcp_server_mediakit/src/mediakit/utils/async_poller.py new file mode 100644 index 00000000..4adf07a3 --- /dev/null +++ b/server/mcp_server_mediakit/src/mediakit/utils/async_poller.py @@ -0,0 +1,36 @@ +from __future__ import annotations + +import asyncio +from typing import Any, Callable + + +async def poll_until_complete( + callback: Callable[[], dict], + *, + poll_interval_seconds: float = 10, + max_poll_attempts: int = 0, + poll_complete: bool = False, +) -> dict: + """轮询任务直到完成或达到最大次数。 + + Args: + callback: 同步查询函数,返回包含 status 字段的 dict + poll_interval_seconds: 轮询间隔(秒) + max_poll_attempts: 最大轮询次数,0 表示仅查一次 + poll_complete: 是否阻塞直到任务完成 + + Returns: + 最终查询结果 dict + """ + attempts = 0 + while True: + result = callback() + status = result.get("status") + if status in {"completed", "failed", "canceled"}: + return result + attempts += 1 + if not poll_complete and max_poll_attempts and attempts >= max_poll_attempts: + return result + if not poll_complete and max_poll_attempts == 0: + return result + await asyncio.sleep(max(poll_interval_seconds, 0)) diff --git a/server/mcp_server_mediakit/src/mediakit/utils/response.py b/server/mcp_server_mediakit/src/mediakit/utils/response.py new file mode 100644 index 00000000..aeb413af --- /dev/null +++ b/server/mcp_server_mediakit/src/mediakit/utils/response.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +from typing import Any + + +def async_task_response(result: dict[str, Any]) -> dict[str, Any]: + """异步处理任务 — 从 API 响应中提取 task_id / request_id 返回。 + + Returns: + { "task_id": "xxx", "request_id": "xxx" } + """ + if not isinstance(result, dict): + return {"task_id": None} + output: dict[str, Any] = {"task_id": result.get("task_id")} + request_id = result.get("request_id") + if request_id is not None: + output["request_id"] = request_id + return output + + +def query_task_response(result: dict[str, Any]) -> dict[str, Any]: + """查询任务结果 — 外层保留 task_id/request_id/status,将 result 中的字段解构平铺到外层。 + + API 返回结构: { task_id, request_id, success, task_type, status, result: { duration, resolution, video_url } } + 处理后返回: { task_id, request_id, status, duration, resolution, video_url } + """ + if not isinstance(result, dict): + return {} + + task_id = result.get("task_id") + request_id = result.get("request_id") + status = result.get("status") + task_result = result.get("result") or {} + + output: dict[str, Any] = {"task_id": task_id} + if request_id is not None: + output["request_id"] = request_id + if status is not None: + output["status"] = status + if isinstance(task_result, dict): + output.update(task_result) + return output + + +def error_response( + error: dict[str, Any] | str | None = None, + *, + task_id: str | None = None, + request_id: str | None = None, +) -> dict[str, Any]: + """报错响应结构。 + + 支持两种输入: + - API 错误响应(dict):提取 error.message 或 error.error 字段 + - 程序异常(str):直接使用异常信息 + + Returns: + { "task_id": "xxx", "request_id": "xxx", "error": "message字段的内容" } + """ + message = "" + if isinstance(error, dict): + message = error.get("message") or error.get("error", "") or error.get("msg", "") + elif isinstance(error, str): + message = error + result: dict[str, Any] = {"error": message} + if task_id is not None: + result["task_id"] = task_id + if request_id is not None: + result["request_id"] = request_id + return result diff --git a/server/mcp_server_mediakit/uv.lock b/server/mcp_server_mediakit/uv.lock new file mode 100644 index 00000000..a7a43f19 --- /dev/null +++ b/server/mcp_server_mediakit/uv.lock @@ -0,0 +1,782 @@ +version = 1 +revision = 3 +requires-python = ">=3.11" + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "anyio" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "certifi" +version = "2026.4.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077, upload-time = "2026-04-22T11:26:11.191Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707, upload-time = "2026-04-22T11:26:09.372Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, + { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, + { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, + { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, + { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, + { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, + { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, + { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, + { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, + { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, +] + +[[package]] +name = "click" +version = "8.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bb/63/f9e1ea081ce35720d8b92acde70daaedace594dc93b693c869e0d5910718/click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2", size = 328061, upload-time = "2026-04-22T15:11:27.506Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", size = 110502, upload-time = "2026-04-22T15:11:25.044Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "cryptography" +version = "48.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/a9/db8f313fdcd85d767d4973515e1db101f9c71f95fced83233de224673757/cryptography-48.0.0.tar.gz", hash = "sha256:5c3932f4436d1cccb036cb0eaef46e6e2db91035166f1ad6505c3c9d5a635920", size = 832984, upload-time = "2026-05-04T22:59:38.133Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/3d/01f6dd9190170a5a241e0e98c2d04be3664a9e6f5b9b872cde63aff1c3dd/cryptography-48.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:0c558d2cdffd8f4bbb30fc7134c74d2ca9a476f830bb053074498fbc86f41ed6", size = 8001587, upload-time = "2026-05-04T22:57:36.803Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6e/e90527eef33f309beb811cf7c982c3aeffcce8e3edb178baa4ca3ae4a6fa/cryptography-48.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5333311663ea94f75dd408665686aaf426563556bb5283554a3539177e03b8c", size = 4690433, upload-time = "2026-05-04T22:57:40.373Z" }, + { url = "https://files.pythonhosted.org/packages/90/04/673510ed51ddff56575f306cf1617d80411ee76831ccd3097599140efdfe/cryptography-48.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7995ef305d7165c3f11ae07f2517e5a4f1d5c18da1376a0a9ed496336b69e5f3", size = 4710620, upload-time = "2026-05-04T22:57:42.935Z" }, + { url = "https://files.pythonhosted.org/packages/14/d5/e9c4ef932c8d800490c34d8bd589d64a31d5890e27ec9e9ad532be893294/cryptography-48.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:40ba1f85eaa6959837b1d51c9767e230e14612eea4ef110ee8854ada22da1bf5", size = 4696283, upload-time = "2026-05-04T22:57:45.294Z" }, + { url = "https://files.pythonhosted.org/packages/0c/29/174b9dfb60b12d59ecfc6cfa04bc88c21b42a54f01b8aae09bb6e51e4c7f/cryptography-48.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:369a6348999f94bbd53435c894377b20ab95f25a9065c283570e70150d8abc3c", size = 5296573, upload-time = "2026-05-04T22:57:47.933Z" }, + { url = "https://files.pythonhosted.org/packages/95/38/0d29a6fd7d0d1373f0c0c88a04ba20e359b257753ac497564cd660fc1d55/cryptography-48.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a0e692c683f4df67815a2d258b324e66f4738bd7a96a218c826dce4f4bd05d8f", size = 4743677, upload-time = "2026-05-04T22:57:50.067Z" }, + { url = "https://files.pythonhosted.org/packages/30/be/eef653013d5c63b6a490529e0316f9ac14a37602965d4903efed1399f32b/cryptography-48.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:18349bbc56f4743c8b12dc32e2bccb2cf83ee8b69a3bba74ef8ae857e26b3d25", size = 4330808, upload-time = "2026-05-04T22:57:52.301Z" }, + { url = "https://files.pythonhosted.org/packages/84/9e/500463e87abb7a0a0f9f256ec21123ecde0a7b5541a15e840ea54551fd81/cryptography-48.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e8eac43dfca5c4cccc6dad9a80504436fca53bb9bc3100a2386d730fbe6b602", size = 4695941, upload-time = "2026-05-04T22:57:54.603Z" }, + { url = "https://files.pythonhosted.org/packages/e3/dc/7303087450c2ec9e7fbb750e17c2abfbc658f23cbd0e54009509b7cc4091/cryptography-48.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9ccdac7d40688ecb5a3b4a604b8a88c8002e3442d6c60aead1db2a89a041560c", size = 5252579, upload-time = "2026-05-04T22:57:57.207Z" }, + { url = "https://files.pythonhosted.org/packages/d0/c0/7101d3b7215edcdc90c45da544961fd8ed2d6448f77577460fa75a8443f7/cryptography-48.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:bd72e68b06bb1e96913f97dd4901119bc17f39d4586a5adf2d3e47bc2b9d58b5", size = 4743326, upload-time = "2026-05-04T22:57:59.535Z" }, + { url = "https://files.pythonhosted.org/packages/ac/d8/5b833bad13016f562ab9d063d68199a4bd121d18458e439515601d3357ec/cryptography-48.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59baa2cb386c4f0b9905bd6eb4c2a79a69a128408fd31d32ca4d7102d4156321", size = 4826672, upload-time = "2026-05-04T22:58:01.996Z" }, + { url = "https://files.pythonhosted.org/packages/98/e1/7074eb8bf3c135558c73fc2bcf0f5633f912e6fb87e868a55c454080ef09/cryptography-48.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9249e3cd978541d665967ac2cb2787fd6a62bddf1e75b3e347a594d7dacf4f74", size = 4972574, upload-time = "2026-05-04T22:58:03.968Z" }, + { url = "https://files.pythonhosted.org/packages/04/70/e5a1b41d325f797f39427aa44ef8baf0be500065ab6d8e10369d850d4a4f/cryptography-48.0.0-cp311-abi3-win32.whl", hash = "sha256:9c459db21422be75e2809370b829a87eb37f74cd785fc4aa9ea1e5f43b47cda4", size = 3294868, upload-time = "2026-05-04T22:58:06.467Z" }, + { url = "https://files.pythonhosted.org/packages/f4/ac/8ac51b4a5fc5932eb7ee5c517ba7dc8cd834f0048962b6b352f00f41ebf9/cryptography-48.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:5b012212e08b8dd5edc78ef54da83dd9892fd9105323b3993eff6bea65dc21d7", size = 3817107, upload-time = "2026-05-04T22:58:08.845Z" }, + { url = "https://files.pythonhosted.org/packages/6b/84/70e3feea9feea87fd7cbe77efb2712ae1e3e6edf10749dc6e95f4e60e455/cryptography-48.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:3cb07a3ed6431663cd321ea8a000a1314c74211f823e4177fefa2255e057d1ec", size = 7986556, upload-time = "2026-05-04T22:58:11.172Z" }, + { url = "https://files.pythonhosted.org/packages/89/6e/18e07a618bb5442ba10cf4df16e99c071365528aa570dfcb8c02e25a303b/cryptography-48.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c7378637d7d88016fa6791c159f698b3d3eed28ebf844ac36b9dc04a14dae18", size = 4684776, upload-time = "2026-05-04T22:58:13.712Z" }, + { url = "https://files.pythonhosted.org/packages/be/6a/4ea3b4c6c6759794d5ee2103c304a5076dc4b19ae1f9fe47dba439e159e9/cryptography-48.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc90c0b39b2e3c65ef52c804b72e3c58f8a04ab2a1871272798e5f9572c17d20", size = 4698121, upload-time = "2026-05-04T22:58:16.448Z" }, + { url = "https://files.pythonhosted.org/packages/2f/59/6ff6ad6cae03bb887da2a5860b2c9805f8dac969ef01ce563336c49bd1d1/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:76341972e1eff8b4bea859f09c0d3e64b96ce931b084f9b9b7db8ef364c30eff", size = 4690042, upload-time = "2026-05-04T22:58:18.544Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b4/fc334ed8cfd705aca282fe4d8f5ae64a8e0f74932e9feecb344610cf6e4d/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:55b7718303bf06a5753dcdccf2f3945cf18ad7bffde41b61226e4db31ab89a9c", size = 5282526, upload-time = "2026-05-04T22:58:20.75Z" }, + { url = "https://files.pythonhosted.org/packages/11/08/9f8c5386cc4cd90d8255c7cdd0f5baf459a08502a09de30dc51f553d38dc/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a64697c641c7b1b2178e573cbc31c7c6684cd56883a478d75143dbb7118036db", size = 4733116, upload-time = "2026-05-04T22:58:23.627Z" }, + { url = "https://files.pythonhosted.org/packages/b8/77/99307d7574045699f8805aa500fa0fb83422d115b5400a064ddd306d7750/cryptography-48.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:561215ea3879cb1cbbf272867e2efda62476f240fb58c64de6b393ae19246741", size = 4316030, upload-time = "2026-05-04T22:58:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/fd/36/a608b98337af3cb2aff4818e406649d30572b7031918b04c87d979495348/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ad64688338ed4bc1a6618076ba75fd7194a5f1797ac60b47afe926285adb3166", size = 4689640, upload-time = "2026-05-04T22:58:27.747Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a6/825010a291b4438aecc1f568bc428189fc1175515223632477c07dc0a6df/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:906cbf0670286c6e0044156bc7d4af9cbb0ef6db9f73e52c3ec56ba6bdde5336", size = 5237657, upload-time = "2026-05-04T22:58:29.848Z" }, + { url = "https://files.pythonhosted.org/packages/b9/09/4e76a09b4caa29aad535ddc806f5d4c5d01885bd978bd984fbc6ca032cae/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:ea8990436d914540a40ab24b6a77c0969695ed52f4a4874c5137ccf7045a7057", size = 4732362, upload-time = "2026-05-04T22:58:32.009Z" }, + { url = "https://files.pythonhosted.org/packages/18/78/444fa04a77d0cb95f417dda20d450e13c56ba8e5220fc892a1658f44f882/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c18684a7f0cc9a3cb60328f496b8e3372def7c5d2df39ac267878b05565aaaae", size = 4819580, upload-time = "2026-05-04T22:58:34.254Z" }, + { url = "https://files.pythonhosted.org/packages/38/85/ea67067c70a1fd4be2c63d35eeed82658023021affccc7b17705f8527dd2/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9be5aafa5736574f8f15f262adc81b2a9869e2cfe9014d52a44633905b40d52c", size = 4963283, upload-time = "2026-05-04T22:58:36.376Z" }, + { url = "https://files.pythonhosted.org/packages/75/54/cc6d0f3deac3e81c7f847e8a189a12b6cdd65059b43dad25d4316abd849a/cryptography-48.0.0-cp314-cp314t-win32.whl", hash = "sha256:c17dfe85494deaeddc5ce251aebd1d60bbe6afc8b62071bb0b469431a000124f", size = 3270954, upload-time = "2026-05-04T22:58:38.791Z" }, + { url = "https://files.pythonhosted.org/packages/49/67/cc947e288c0758a4e5473d1dcb743037ab7785541265a969240b8885441a/cryptography-48.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27241b1dc9962e056062a8eef1991d02c3a24569c95975bd2322a8a52c6e5e12", size = 3797313, upload-time = "2026-05-04T22:58:40.746Z" }, + { url = "https://files.pythonhosted.org/packages/f2/63/61d4a4e1c6b6bab6ce1e213cd36a24c415d90e76d78c5eb8577c5541d2e8/cryptography-48.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:58d00498e8933e4a194f3076aee1b4a97dfec1a6da444535755822fe5d8b0b86", size = 7983482, upload-time = "2026-05-04T22:58:43.769Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ac/f5b5995b87770c693e2596559ffafe195b4033a57f14a82268a2842953f3/cryptography-48.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:614d0949f4790582d2cc25553abd09dd723025f0c0e7c67376a1d77196743d6e", size = 4683266, upload-time = "2026-05-04T22:58:46.064Z" }, + { url = "https://files.pythonhosted.org/packages/ec/c6/8b14f67e18338fbc4adb76f66c001f5c3610b3e2d1837f268f47a347dbbb/cryptography-48.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ce4bfae76319a532a2dc68f82cc32f5676ee792a983187dac07183690e5c66f", size = 4696228, upload-time = "2026-05-04T22:58:48.22Z" }, + { url = "https://files.pythonhosted.org/packages/ea/73/f808fbae9514bd91b47875b003f13e284c8c6bdfd904b7944e803937eec1/cryptography-48.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2eb992bbd4661238c5a397594c83f5b4dc2bc5b848c365c8f991b6780efcc5c7", size = 4689097, upload-time = "2026-05-04T22:58:50.9Z" }, + { url = "https://files.pythonhosted.org/packages/93/01/d86632d7d28db8ae83221995752eeb6639ffb374c2d22955648cf8d52797/cryptography-48.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:22a5cb272895dce158b2cacdfdc3debd299019659f42947dbdac6f32d68fe832", size = 5283582, upload-time = "2026-05-04T22:58:53.017Z" }, + { url = "https://files.pythonhosted.org/packages/02/e1/50edc7a50334807cc4791fc4a0ce7468b4a1416d9138eab358bfc9a3d70b/cryptography-48.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2b4d59804e8408e2fea7d1fbaf218e5ec984325221db76e6a241a9abd6cdd95c", size = 4730479, upload-time = "2026-05-04T22:58:55.611Z" }, + { url = "https://files.pythonhosted.org/packages/6f/af/99a582b1b1641ff5911ac559beb45097cf79efd4ead4657f578ef1af2d47/cryptography-48.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:984a20b0f62a26f48a3396c72e4bc34c66e356d356bf370053066b3b6d54634a", size = 4326481, upload-time = "2026-05-04T22:58:57.607Z" }, + { url = "https://files.pythonhosted.org/packages/90/ee/89aa26a06ef0a7d7611788ffd571a7c50e368cc6a4d5eef8b4884e866edb/cryptography-48.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5a5ed8fde7a1d09376ca0b40e68cd59c69fe23b1f9768bd5824f54681626032a", size = 4688713, upload-time = "2026-05-04T22:59:00.077Z" }, + { url = "https://files.pythonhosted.org/packages/70/ba/bcb1b0bb7a33d4c7c0c4d4c7874b4a62ae4f56113a5f4baefa362dfb1f0f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:8cd666227ef7af430aa5914a9910e0ddd703e75f039cef0825cd0da71b6b711a", size = 5238165, upload-time = "2026-05-04T22:59:02.317Z" }, + { url = "https://files.pythonhosted.org/packages/c9/70/ca4003b1ce5ca3dc3186ada51908c8a9b9ff7d5cab83cc0d43ee14ec144f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9071196d81abc88b3516ac8cdfad32e2b66dd4a5393a8e68a961e9161ddc6239", size = 4729947, upload-time = "2026-05-04T22:59:05.255Z" }, + { url = "https://files.pythonhosted.org/packages/44/a0/4ec7cf774207905aef1a8d11c3750d5a1db805eb380ee4e16df317870128/cryptography-48.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e2d54c8be6152856a36f0882ab231e70f8ec7f14e93cf87db8a2ed056bf160c", size = 4822059, upload-time = "2026-05-04T22:59:07.802Z" }, + { url = "https://files.pythonhosted.org/packages/1e/75/a2e55f99c16fcac7b5d6c1eb19ad8e00799854d6be5ca845f9259eae1681/cryptography-48.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a5da777e32ffed6f85a7b2b3f7c5cbc88c146bfcd0a1d7baf5fcc6c52ee35dd4", size = 4960575, upload-time = "2026-05-04T22:59:09.851Z" }, + { url = "https://files.pythonhosted.org/packages/b8/23/6e6f32143ab5d8b36ca848a502c4bcd477ae75b9e1677e3530d669062578/cryptography-48.0.0-cp39-abi3-win32.whl", hash = "sha256:77a2ccbbe917f6710e05ba9adaa25fb5075620bf3ea6fb751997875aff4ae4bd", size = 3279117, upload-time = "2026-05-04T22:59:12.019Z" }, + { url = "https://files.pythonhosted.org/packages/9d/9a/0fea98a70cf1749d41d738836f6349d97945f7c89433a259a6c2642eefeb/cryptography-48.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:16cd65b9330583e4619939b3a3843eec1e6e789744bb01e7c7e2e62e33c239c8", size = 3792100, upload-time = "2026-05-04T22:59:14.884Z" }, + { url = "https://files.pythonhosted.org/packages/be/d2/024b5e06be9d44cb021fb0e1a03d34d63989cf56a0fe62f3dfbab695b9b4/cryptography-48.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:84cf79f0dc8b36ac5da873481716e87aef31fcfa0444f9e1d8b4b2cece142855", size = 3950391, upload-time = "2026-05-04T22:59:17.415Z" }, + { url = "https://files.pythonhosted.org/packages/bc/17/3861e17c56fa0fd37491a14a8673fdb77c57fc5693cafe745ea8b06dba75/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:fdfef35d751d510fcef5252703621574364fec16418c4a1e5e1055248401054b", size = 4637126, upload-time = "2026-05-04T22:59:20.197Z" }, + { url = "https://files.pythonhosted.org/packages/f0/0a/7e226dbff530f21480727eb764973a7bff2b912f8e15cd4f129e71b56d1d/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:0890f502ddf7d9c6426129c3f49f5c0a39278ed7cd6322c8755ffca6ee675a13", size = 4667270, upload-time = "2026-05-04T22:59:22.647Z" }, + { url = "https://files.pythonhosted.org/packages/3b/f2/5a72274ca9f1b2a8b44a662ee0bf1b435909deb473d6f97bcd035bcdbc71/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:ecde28a596bead48b0cfd2a1b4416c3d43074c2d785e3a398d7ec1fc4d0f7fbb", size = 4636797, upload-time = "2026-05-04T22:59:24.912Z" }, + { url = "https://files.pythonhosted.org/packages/b4/e1/48cedb2fe63626e91ded1edad159e2a4fb8b6906c4425eb7749673077ce7/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:4defde8685ae324a9eb9d818717e93b4638ef67070ac9bc15b8ca85f63048355", size = 4666800, upload-time = "2026-05-04T22:59:27.474Z" }, + { url = "https://files.pythonhosted.org/packages/a2/ca/7e8365deec19afb2b2c7be7c1c0aa8f99633b54e90c570999acda93260fc/cryptography-48.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:db63bf618e5dea46c07de12e900fe1cdd2541e6dc9dbae772a70b7d4d4765f6a", size = 3739536, upload-time = "2026-05-04T22:59:29.61Z" }, +] + +[[package]] +name = "decorator" +version = "5.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "httpx-sse" +version = "0.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, +] + +[[package]] +name = "idna" +version = "3.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/b1/efac073e0c297ecf2fb33c346989a529d4e19164f1759102dee5953ee17e/idna-3.14.tar.gz", hash = "sha256:466d810d7a2cc1022bea9b037c39728d51ae7dad40d480fc9b7d7ecf98ba8ee3", size = 198272, upload-time = "2026-05-10T20:32:15.935Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/3c/3f62dee257eb3d6b2c1ef2a09d36d9793c7111156a73b5654d2c2305e5ce/idna-3.14-py3-none-any.whl", hash = "sha256:e677eaf072e290f7b725f9acf0b3a2bd55f9fd6f7c70abe5f0e34823d0accf69", size = 72184, upload-time = "2026-05-10T20:32:14.295Z" }, +] + +[[package]] +name = "jsonschema" +version = "4.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, +] + +[[package]] +name = "mcp" +version = "1.27.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "httpx" }, + { name = "httpx-sse" }, + { name = "jsonschema" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "pyjwt", extra = ["crypto"] }, + { name = "python-multipart" }, + { name = "pywin32", marker = "sys_platform == 'win32'" }, + { name = "sse-starlette" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, + { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/83/d1efe7c2980d8a3afa476f4e3d42d53dd54c0ab94c27bee5d755b45c8b73/mcp-1.27.1.tar.gz", hash = "sha256:0f47e1820f8f8f941466b39749eb1d1839a04caddca2bc60e9d46e8a99914924", size = 608458, upload-time = "2026-05-08T16:50:12.601Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/73/42d9596facebdb533b7f0b86c1b0364ef350d1f8ba78b1052e8a58b48b65/mcp-1.27.1-py3-none-any.whl", hash = "sha256:1af3c4203b329430fde7a87b4fcb6392a041f5cb851fd68fc674016ab4e7c06f", size = 216260, upload-time = "2026-05-08T16:50:10.547Z" }, +] + +[[package]] +name = "mcp-server-mediakit" +version = "1.0.0" +source = { editable = "." } +dependencies = [ + { name = "httpx" }, + { name = "mcp" }, + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "pyyaml" }, + { name = "retry" }, +] + +[package.metadata] +requires-dist = [ + { name = "httpx", specifier = ">=0.25.0" }, + { name = "mcp", specifier = ">=1.12.0" }, + { name = "pydantic", specifier = ">=2.0.0" }, + { name = "python-dotenv", specifier = ">=1.0.0" }, + { name = "pyyaml", specifier = ">=6.0" }, + { name = "retry", specifier = ">=0.9.2" }, +] + +[[package]] +name = "py" +version = "1.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/ff/fec109ceb715d2a6b4c4a85a61af3b40c723a961e8828319fbcb15b868dc/py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719", size = 207796, upload-time = "2021-11-04T17:17:01.377Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378", size = 98708, upload-time = "2021-11-04T17:17:00.152Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pydantic" +version = "2.13.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.46.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/fa/6d7708d2cfc1a832acb6aeb0cd16e801902df8a0f583bb3b4b527fde022e/pydantic_core-2.46.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594", size = 2111872, upload-time = "2026-05-06T13:40:27.596Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6f/aa064a3e74b5745afbdf250594f38e7ead05e2d651bcb35994b9417a0d4d/pydantic_core-2.46.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c", size = 1948255, upload-time = "2026-05-06T13:39:12.574Z" }, + { url = "https://files.pythonhosted.org/packages/43/3a/41114a9f7569b84b4d84e7a018c57c56347dac30c0d4a872946ec4e36c46/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826", size = 1972827, upload-time = "2026-05-06T13:38:19.841Z" }, + { url = "https://files.pythonhosted.org/packages/ef/25/1ab42e8048fe551934d9884e8d64daa7e990ad386f310a15981aeb6a5b08/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04", size = 2041051, upload-time = "2026-05-06T13:38:10.447Z" }, + { url = "https://files.pythonhosted.org/packages/94/c2/1a934597ddf08da410385b3b7aae91956a5a76c635effef456074fad7e88/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e", size = 2221314, upload-time = "2026-05-06T13:40:13.089Z" }, + { url = "https://files.pythonhosted.org/packages/02/6d/9e8ad178c9c4df27ad3c8f25d1fe2a7ab0d2ba0559fad4aee5d3d1f16771/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3", size = 2285146, upload-time = "2026-05-06T13:38:59.224Z" }, + { url = "https://files.pythonhosted.org/packages/80/50/540cd3aeefc041beb111125c4bff779831a2111fc6b15a9138cda277d32c/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4", size = 2089685, upload-time = "2026-05-06T13:38:17.762Z" }, + { url = "https://files.pythonhosted.org/packages/6b/a4/b440ad35f05f6a38f89fa0f149accb3f0e02be94ca5e15f3c449a61b4bc9/pydantic_core-2.46.4-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398", size = 2115420, upload-time = "2026-05-06T13:37:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/99/61/de4f55db8dfd57bfdfa9a12ec90fe1b57c4f41062f7ca86f08586b3e0ac0/pydantic_core-2.46.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3", size = 2165122, upload-time = "2026-05-06T13:37:01.167Z" }, + { url = "https://files.pythonhosted.org/packages/f7/52/7c529d7bdb2d1068bd52f51fe32572c8301f9a4febf1948f10639f1436f5/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848", size = 2182573, upload-time = "2026-05-06T13:38:45.04Z" }, + { url = "https://files.pythonhosted.org/packages/37/b3/7c40325848ba78247f2812dcf9c7274e38cd801820ca6dd9fe63bcfb0eb4/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3", size = 2317139, upload-time = "2026-05-06T13:37:15.539Z" }, + { url = "https://files.pythonhosted.org/packages/d9/37/f913f81a657c865b75da6c0dbed79876073c2a43b5bd9edbe8da785e4d49/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109", size = 2360433, upload-time = "2026-05-06T13:37:30.099Z" }, + { url = "https://files.pythonhosted.org/packages/c4/67/6acaa1be2567f9256b056d8477158cac7240813956ce86e49deae8e173b4/pydantic_core-2.46.4-cp311-cp311-win32.whl", hash = "sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda", size = 1985513, upload-time = "2026-05-06T13:38:15.669Z" }, + { url = "https://files.pythonhosted.org/packages/aa/e6/c505f83dfeda9a2e5c995cfd872949e4d05e12f7feb3dca72f633daefa94/pydantic_core-2.46.4-cp311-cp311-win_amd64.whl", hash = "sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33", size = 2071114, upload-time = "2026-05-06T13:40:35.416Z" }, + { url = "https://files.pythonhosted.org/packages/0f/da/7a263a96d965d9d0df5e8de8a475f33495451117035b09acb110288c381f/pydantic_core-2.46.4-cp311-cp311-win_arm64.whl", hash = "sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d", size = 2044298, upload-time = "2026-05-06T13:38:29.754Z" }, + { url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" }, + { url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" }, + { url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" }, + { url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" }, + { url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" }, + { url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" }, + { url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" }, + { url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" }, + { url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" }, + { url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" }, + { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" }, + { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" }, + { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" }, + { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" }, + { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" }, + { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" }, + { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" }, + { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" }, + { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" }, + { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" }, + { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" }, + { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" }, + { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" }, + { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" }, + { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" }, + { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" }, + { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" }, + { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" }, + { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" }, + { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" }, + { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" }, + { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" }, + { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" }, + { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" }, + { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" }, + { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" }, + { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" }, + { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" }, + { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" }, + { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" }, + { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" }, + { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" }, + { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" }, + { url = "https://files.pythonhosted.org/packages/ee/a4/73995fd4ebbb46ba0ee51e6fa049b8f02c40daebb762208feda8a6b7894d/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c", size = 2111589, upload-time = "2026-05-06T13:37:10.817Z" }, + { url = "https://files.pythonhosted.org/packages/fb/7f/f37d3a5e8bfcc2e403f5c57a730f2d815693fb42119e8ea48b3789335af1/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b", size = 1944552, upload-time = "2026-05-06T13:36:56.717Z" }, + { url = "https://files.pythonhosted.org/packages/15/3c/d7eb777b3ff43e8433a4efb39a17aa8fd98a4ee8561a24a67ef5db07b2d6/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b", size = 1982984, upload-time = "2026-05-06T13:39:06.207Z" }, + { url = "https://files.pythonhosted.org/packages/63/87/70b9f40170a81afd55ca26c9b2acb25c20d64bcfbf888fafecb3ba077d4c/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea", size = 2138417, upload-time = "2026-05-06T13:39:45.476Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" }, + { url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" }, + { url = "https://files.pythonhosted.org/packages/11/cb/428de0385b6c8d44b716feba566abfacfbd23ee3c4439faa789a1456242f/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0", size = 2112782, upload-time = "2026-05-06T13:37:04.016Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b5/6a17bdadd0fc1f170adfd05a20d37c832f52b117b4d9131da1f41bb097ce/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7", size = 1952146, upload-time = "2026-05-06T13:39:43.092Z" }, + { url = "https://files.pythonhosted.org/packages/2a/dc/03734d80e362cd43ef65428e9de77c730ce7f2f11c60d2b1e1b39f0fbf99/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2", size = 2134492, upload-time = "2026-05-06T13:36:58.124Z" }, + { url = "https://files.pythonhosted.org/packages/de/df/5e5ffc085ed07cc22d298134d3d911c63e91f6a0eb91fe646750a3209910/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9", size = 2156604, upload-time = "2026-05-06T13:37:49.88Z" }, + { url = "https://files.pythonhosted.org/packages/81/44/6e112a4253e56f5705467cbab7ab5e91ee7398ba3d56d358635958893d3e/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf", size = 2183828, upload-time = "2026-05-06T13:37:43.053Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ad/5565071e937d8e752842ac241463944c9eb14c87e2d269f2658a5bd05e98/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30", size = 2310000, upload-time = "2026-05-06T13:37:56.694Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c3/66883a5cec183e7fba4d024b4cbbe61851a63750ef606b0afecc46d1f2bf/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc", size = 2361286, upload-time = "2026-05-06T13:40:05.667Z" }, + { url = "https://files.pythonhosted.org/packages/4b/2d/69abac8f838090bbecd5df894befb2c2619e7996a98ddb949db9f3b93225/pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983", size = 2193071, upload-time = "2026-05-06T13:38:08.682Z" }, +] + +[[package]] +name = "pydantic-settings" +version = "2.14.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/07/60/1d1e59c9c90d54591469ada7d268251f71c24bdb765f1a8a832cee8c6653/pydantic_settings-2.14.1.tar.gz", hash = "sha256:e874d3bec7e787b0c9958277956ed9b4dd5de6a80e162188fdaff7c5e26fd5fa", size = 235551, upload-time = "2026-05-08T13:40:06.542Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/8d/f1af3832f5e6eb13ba94ee809e72b8ecb5eef226d27ee0bef7d963d943c7/pydantic_settings-2.14.1-py3-none-any.whl", hash = "sha256:6e3c7edfd8277687cdc598f56e5cff0e9bfff0910a3749deaa8d4401c3a2b9de", size = 60964, upload-time = "2026-05-08T13:40:04.958Z" }, +] + +[[package]] +name = "pyjwt" +version = "2.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" }, +] + +[package.optional-dependencies] +crypto = [ + { name = "cryptography" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, +] + +[[package]] +name = "python-multipart" +version = "0.0.28" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/54/a85eb421fbdd5007bc5af39d0f4ed9fa609e0fedbfdc2adcf0b34526870e/python_multipart-0.0.28.tar.gz", hash = "sha256:8550da197eac0f7ab748961fc9509b999fa2662ea25cef857f05249f6893c0f8", size = 45314, upload-time = "2026-05-10T11:05:16.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/a2/43bbc5860b5034e2af4ef99a0e04d726ff329c43e192ef3abaa8d7ecfce5/python_multipart-0.0.28-py3-none-any.whl", hash = "sha256:10faac07eb966c3f48dc415f9dee46c04cb10d58d30a35677db8027c825ed9b6", size = 29438, upload-time = "2026-05-10T11:05:15.052Z" }, +] + +[[package]] +name = "pywin32" +version = "311" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151", size = 8697031, upload-time = "2025-07-14T20:13:13.266Z" }, + { url = "https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503", size = 9508308, upload-time = "2025-07-14T20:13:15.147Z" }, + { url = "https://files.pythonhosted.org/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2", size = 8703930, upload-time = "2025-07-14T20:13:16.945Z" }, + { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543, upload-time = "2025-07-14T20:13:20.765Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040, upload-time = "2025-07-14T20:13:22.543Z" }, + { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102, upload-time = "2025-07-14T20:13:24.682Z" }, + { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700, upload-time = "2025-07-14T20:13:26.471Z" }, + { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700, upload-time = "2025-07-14T20:13:28.243Z" }, + { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318, upload-time = "2025-07-14T20:13:30.348Z" }, + { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" }, + { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" }, + { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "referencing" +version = "0.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, +] + +[[package]] +name = "retry" +version = "0.9.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "decorator" }, + { name = "py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/72/75d0b85443fbc8d9f38d08d2b1b67cc184ce35280e4a3813cda2f445f3a4/retry-0.9.2.tar.gz", hash = "sha256:f8bfa8b99b69c4506d6f5bd3b0aabf77f98cdb17f3c9fc3f5ca820033336fba4", size = 6448, upload-time = "2016-05-11T13:58:51.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/0d/53aea75710af4528a25ed6837d71d117602b01946b307a3912cb3cfcbcba/retry-0.9.2-py2.py3-none-any.whl", hash = "sha256:ccddf89761fa2c726ab29391837d4327f819ea14d244c232a1d24c67a2f98606", size = 7986, upload-time = "2016-05-11T13:58:39.925Z" }, +] + +[[package]] +name = "rpds-py" +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/6e/f964e88b3d2abee2a82c1ac8366da848fce1c6d834dc2132c3fda3970290/rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425", size = 370157, upload-time = "2025-11-30T20:21:53.789Z" }, + { url = "https://files.pythonhosted.org/packages/94/ba/24e5ebb7c1c82e74c4e4f33b2112a5573ddc703915b13a073737b59b86e0/rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d", size = 359676, upload-time = "2025-11-30T20:21:55.475Z" }, + { url = "https://files.pythonhosted.org/packages/84/86/04dbba1b087227747d64d80c3b74df946b986c57af0a9f0c98726d4d7a3b/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4", size = 389938, upload-time = "2025-11-30T20:21:57.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/bb/1463f0b1722b7f45431bdd468301991d1328b16cffe0b1c2918eba2c4eee/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f", size = 402932, upload-time = "2025-11-30T20:21:58.47Z" }, + { url = "https://files.pythonhosted.org/packages/99/ee/2520700a5c1f2d76631f948b0736cdf9b0acb25abd0ca8e889b5c62ac2e3/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4", size = 525830, upload-time = "2025-11-30T20:21:59.699Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ad/bd0331f740f5705cc555a5e17fdf334671262160270962e69a2bdef3bf76/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97", size = 412033, upload-time = "2025-11-30T20:22:00.991Z" }, + { url = "https://files.pythonhosted.org/packages/f8/1e/372195d326549bb51f0ba0f2ecb9874579906b97e08880e7a65c3bef1a99/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89", size = 390828, upload-time = "2025-11-30T20:22:02.723Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2b/d88bb33294e3e0c76bc8f351a3721212713629ffca1700fa94979cb3eae8/rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d", size = 404683, upload-time = "2025-11-30T20:22:04.367Z" }, + { url = "https://files.pythonhosted.org/packages/50/32/c759a8d42bcb5289c1fac697cd92f6fe01a018dd937e62ae77e0e7f15702/rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038", size = 421583, upload-time = "2025-11-30T20:22:05.814Z" }, + { url = "https://files.pythonhosted.org/packages/2b/81/e729761dbd55ddf5d84ec4ff1f47857f4374b0f19bdabfcf929164da3e24/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7", size = 572496, upload-time = "2025-11-30T20:22:07.713Z" }, + { url = "https://files.pythonhosted.org/packages/14/f6/69066a924c3557c9c30baa6ec3a0aa07526305684c6f86c696b08860726c/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed", size = 598669, upload-time = "2025-11-30T20:22:09.312Z" }, + { url = "https://files.pythonhosted.org/packages/5f/48/905896b1eb8a05630d20333d1d8ffd162394127b74ce0b0784ae04498d32/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85", size = 561011, upload-time = "2025-11-30T20:22:11.309Z" }, + { url = "https://files.pythonhosted.org/packages/22/16/cd3027c7e279d22e5eb431dd3c0fbc677bed58797fe7581e148f3f68818b/rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c", size = 221406, upload-time = "2025-11-30T20:22:13.101Z" }, + { url = "https://files.pythonhosted.org/packages/fa/5b/e7b7aa136f28462b344e652ee010d4de26ee9fd16f1bfd5811f5153ccf89/rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825", size = 236024, upload-time = "2025-11-30T20:22:14.853Z" }, + { url = "https://files.pythonhosted.org/packages/14/a6/364bba985e4c13658edb156640608f2c9e1d3ea3c81b27aa9d889fff0e31/rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229", size = 229069, upload-time = "2025-11-30T20:22:16.577Z" }, + { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, + { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, + { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, + { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, + { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, + { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, + { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, + { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, + { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, + { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, + { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, + { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, + { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, + { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, + { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, + { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, + { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, + { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, + { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, + { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, + { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, + { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, + { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, + { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, + { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, + { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, + { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, + { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, + { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, + { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, + { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, + { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, + { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, + { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, + { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, + { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, + { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, + { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, + { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, + { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, + { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, + { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, + { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, + { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, + { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, + { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, + { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, + { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, + { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, + { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, + { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, + { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, + { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, + { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, + { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, + { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, + { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, + { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, + { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, + { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, + { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, + { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, + { url = "https://files.pythonhosted.org/packages/69/71/3f34339ee70521864411f8b6992e7ab13ac30d8e4e3309e07c7361767d91/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58", size = 372292, upload-time = "2025-11-30T20:24:16.537Z" }, + { url = "https://files.pythonhosted.org/packages/57/09/f183df9b8f2d66720d2ef71075c59f7e1b336bec7ee4c48f0a2b06857653/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a", size = 362128, upload-time = "2025-11-30T20:24:18.086Z" }, + { url = "https://files.pythonhosted.org/packages/7a/68/5c2594e937253457342e078f0cc1ded3dd7b2ad59afdbf2d354869110a02/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb", size = 391542, upload-time = "2025-11-30T20:24:20.092Z" }, + { url = "https://files.pythonhosted.org/packages/49/5c/31ef1afd70b4b4fbdb2800249f34c57c64beb687495b10aec0365f53dfc4/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c", size = 404004, upload-time = "2025-11-30T20:24:22.231Z" }, + { url = "https://files.pythonhosted.org/packages/e3/63/0cfbea38d05756f3440ce6534d51a491d26176ac045e2707adc99bb6e60a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3", size = 527063, upload-time = "2025-11-30T20:24:24.302Z" }, + { url = "https://files.pythonhosted.org/packages/42/e6/01e1f72a2456678b0f618fc9a1a13f882061690893c192fcad9f2926553a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5", size = 413099, upload-time = "2025-11-30T20:24:25.916Z" }, + { url = "https://files.pythonhosted.org/packages/b8/25/8df56677f209003dcbb180765520c544525e3ef21ea72279c98b9aa7c7fb/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738", size = 392177, upload-time = "2025-11-30T20:24:27.834Z" }, + { url = "https://files.pythonhosted.org/packages/4a/b4/0a771378c5f16f8115f796d1f437950158679bcd2a7c68cf251cfb00ed5b/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f", size = 406015, upload-time = "2025-11-30T20:24:29.457Z" }, + { url = "https://files.pythonhosted.org/packages/36/d8/456dbba0af75049dc6f63ff295a2f92766b9d521fa00de67a2bd6427d57a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877", size = 423736, upload-time = "2025-11-30T20:24:31.22Z" }, + { url = "https://files.pythonhosted.org/packages/13/64/b4d76f227d5c45a7e0b796c674fd81b0a6c4fbd48dc29271857d8219571c/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a", size = 573981, upload-time = "2025-11-30T20:24:32.934Z" }, + { url = "https://files.pythonhosted.org/packages/20/91/092bacadeda3edf92bf743cc96a7be133e13a39cdbfd7b5082e7ab638406/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4", size = 599782, upload-time = "2025-11-30T20:24:35.169Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191, upload-time = "2025-11-30T20:24:36.853Z" }, +] + +[[package]] +name = "sse-starlette" +version = "3.4.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "starlette" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/64/13/3cafb96bceb02949f265bbdf1cbcea2810271ae709e4aa35e980f90c07fd/sse_starlette-3.4.3.tar.gz", hash = "sha256:a7f6d87cf482cf38b911c31075811c7f8b4efbada8ac9d5199a8e239fed513c9", size = 35247, upload-time = "2026-05-11T17:23:41.987Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/a4/c888212b19dd432110d4a78dbc5e6c1bc7476e5fff2f2df2ea9f298b0003/sse_starlette-3.4.3-py3-none-any.whl", hash = "sha256:bf8a90d76192062f01b55095593606bfc7edd0e3ad481339a6e16e7890bc9367", size = 16514, upload-time = "2026-05-11T17:23:40.352Z" }, +] + +[[package]] +name = "starlette" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/81/69/17425771797c36cded50b7fe44e850315d039f28b15901ab44839e70b593/starlette-1.0.0.tar.gz", hash = "sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149", size = 2655289, upload-time = "2026-03-22T18:29:46.779Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/c9/584bc9651441b4ba60cc4d557d8a547b5aff901af35bda3a4ee30c819b82/starlette-1.0.0-py3-none-any.whl", hash = "sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b", size = 72651, upload-time = "2026-03-22T18:29:45.111Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.46.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1f/93/041fca8274050e40e6791f267d82e0e2e27dd165627bd640d3e0e378d877/uvicorn-0.46.0.tar.gz", hash = "sha256:fb9da0926999cc6cb22dc7cd71a94a632f078e6ae47ff683c5c420750fb7413d", size = 88758, upload-time = "2026-04-23T07:16:00.151Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/a3/5b1562db76a5a488274b2332a97199b32d0442aca0ed193697fd47786316/uvicorn-0.46.0-py3-none-any.whl", hash = "sha256:bbebbcbed972d162afca128605223022bedd345b7bc7855ce66deb31487a9048", size = 70926, upload-time = "2026-04-23T07:15:58.355Z" }, +] From 00bc07eef3fa928cb6da8baa0bfd75722145b57f Mon Sep 17 00:00:00 2001 From: tiehongji Date: Mon, 18 May 2026 18:58:43 +0800 Subject: [PATCH 2/6] =?UTF-8?q?feat:=E9=94=99=E8=AF=AF=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/mediakit/utils/response.py | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/server/mcp_server_mediakit/src/mediakit/utils/response.py b/server/mcp_server_mediakit/src/mediakit/utils/response.py index aeb413af..ca92d8df 100644 --- a/server/mcp_server_mediakit/src/mediakit/utils/response.py +++ b/server/mcp_server_mediakit/src/mediakit/utils/response.py @@ -6,11 +6,22 @@ def async_task_response(result: dict[str, Any]) -> dict[str, Any]: """异步处理任务 — 从 API 响应中提取 task_id / request_id 返回。 + 业务级失败(HTTP 2xx 但 success=false)会被识别并转为 error 字段输出。 + Returns: - { "task_id": "xxx", "request_id": "xxx" } + 正向: { "task_id": "xxx", "request_id": "xxx" } + 失败: { "task_id": "xxx", "request_id": "xxx", "error": "" } """ if not isinstance(result, dict): return {"task_id": None} + + if result.get("success") is False: + return error_response( + result.get("error"), + task_id=result.get("task_id"), + request_id=result.get("request_id"), + ) + output: dict[str, Any] = {"task_id": result.get("task_id")} request_id = result.get("request_id") if request_id is not None: @@ -21,12 +32,22 @@ def async_task_response(result: dict[str, Any]) -> dict[str, Any]: def query_task_response(result: dict[str, Any]) -> dict[str, Any]: """查询任务结果 — 外层保留 task_id/request_id/status,将 result 中的字段解构平铺到外层。 + 业务级失败(HTTP 2xx 但 success=false,如 task_id 不存在)会被识别并转为 error 字段输出。 + API 返回结构: { task_id, request_id, success, task_type, status, result: { duration, resolution, video_url } } - 处理后返回: { task_id, request_id, status, duration, resolution, video_url } + 正向输出: { task_id, request_id, status, duration, resolution, video_url } + 失败输出: { task_id, request_id, error: "" } """ if not isinstance(result, dict): return {} + if result.get("success") is False: + return error_response( + result.get("error"), + task_id=result.get("task_id"), + request_id=result.get("request_id"), + ) + task_id = result.get("task_id") request_id = result.get("request_id") status = result.get("status") @@ -54,14 +75,18 @@ def error_response( - API 错误响应(dict):提取 error.message 或 error.error 字段 - 程序异常(str):直接使用异常信息 + 任何兜底场景均会回退为 "unknown error",确保 error 字段非空。 + Returns: { "task_id": "xxx", "request_id": "xxx", "error": "message字段的内容" } """ message = "" if isinstance(error, dict): - message = error.get("message") or error.get("error", "") or error.get("msg", "") + message = error.get("message") or error.get("error", "") or error.get("msg", "") elif isinstance(error, str): message = error + if not message: + message = "unknown error" result: dict[str, Any] = {"error": message} if task_id is not None: result["task_id"] = task_id From 1cb4fe4857cd1ebed6f0bc9cadb0e5dbb7283add Mon Sep 17 00:00:00 2001 From: tiehongji Date: Wed, 20 May 2026 11:23:58 +0800 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20=E5=BC=82=E5=B8=B8=E6=8D=95?= =?UTF-8?q?=E8=8E=B7=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/mediakit/utils/response.py | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/server/mcp_server_mediakit/src/mediakit/utils/response.py b/server/mcp_server_mediakit/src/mediakit/utils/response.py index ca92d8df..19f34875 100644 --- a/server/mcp_server_mediakit/src/mediakit/utils/response.py +++ b/server/mcp_server_mediakit/src/mediakit/utils/response.py @@ -71,23 +71,14 @@ def error_response( ) -> dict[str, Any]: """报错响应结构。 - 支持两种输入: - - API 错误响应(dict):提取 error.message 或 error.error 字段 - - 程序异常(str):直接使用异常信息 - - 任何兜底场景均会回退为 "unknown error",确保 error 字段非空。 + 直接透传原始 error 内容,不做字段提取。 Returns: - { "task_id": "xxx", "request_id": "xxx", "error": "message字段的内容" } + { "task_id": "xxx", "request_id": "xxx", "error": <原始error> } """ - message = "" - if isinstance(error, dict): - message = error.get("message") or error.get("error", "") or error.get("msg", "") - elif isinstance(error, str): - message = error - if not message: - message = "unknown error" - result: dict[str, Any] = {"error": message} + if not error: + error = "unknown error" + result: dict[str, Any] = {"error": error} if task_id is not None: result["task_id"] = task_id if request_id is not None: From 750c559d2eced78141e7920a77442151e0e580a2 Mon Sep 17 00:00:00 2001 From: tiehongji Date: Tue, 26 May 2026 19:41:37 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E4=BF=AE=E6=94=B9readme=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/mcp_server_mediakit/README.md | 283 +++++++++++++++++++----- server/mcp_server_mediakit/README_zh.md | 234 +++++++++++++++++--- 2 files changed, 427 insertions(+), 90 deletions(-) diff --git a/server/mcp_server_mediakit/README.md b/server/mcp_server_mediakit/README.md index 8321a5b4..d41f4480 100644 --- a/server/mcp_server_mediakit/README.md +++ b/server/mcp_server_mediakit/README.md @@ -1,6 +1,6 @@ # MediaKit MCP Server -MediaKit MCP Server is a standard AI capability plugin for Volcano Engine AI MediaKit. It is built on the MCP (Model Context Protocol) protocol and exposes cloud media capabilities such as video editing, audio/video processing, subtitle processing, and video enhancement as tools that can be called by AI agents. With MediaKit MCP, developers can use natural language to drive intelligent media production workflows. +MediaKit MCP Server is a standard AI capability plugin for Volcano Engine AI MediaKit. It is built on the MCP (Model Context Protocol) protocol and exposes cloud media capabilities such as video editing, audio processing, subtitle processing, and video enhancement as tools that can be called by AI agents. With MediaKit MCP, developers can use natural language to drive intelligent media production workflows. | Field | Value | | --- | --- | @@ -11,7 +11,7 @@ MediaKit MCP Server is a standard AI capability plugin for Volcano Engine AI Med ## Tool Overview -MediaKit MCP provides tools that cover the full workflow from asynchronous task query to deep media editing and video enhancement. Tools are grouped by domain and can be loaded dynamically by group or by tool name. +MediaKit MCP provides tools that cover the full workflow from asynchronous task query to deep media editing and video enhancement. All tools support dynamic loading by group or by tool name to optimize agent reasoning efficiency. @@ -27,136 +27,301 @@ MediaKit MCP provides tools that cover the full workflow from asynchronous task - - - - - - - - - - -
Shared shared query_taskTask query: Query asynchronous task status and results after submitting an asynchronous MediaKit task. For detailed input and output parameters, see - query_task. + Task query: Query asynchronous task status and results after submitting an asynchronous task. For detailed input and output parameters, see + query_task.
Video editing editing add_image_to_videoAdd image to video: Add an image overlay or watermark to a video. For detailed input and output parameters, see - add_image_to_video. + Add image to video: Overlay an image on a video, commonly used for image watermarks. For detailed input and output parameters, see + add_image_to_video.
add_subtitle_to_video Add subtitles to video: Burn subtitle files or subtitle text into a video. For detailed input and output parameters, see - add_subtitle_to_video. + add_subtitle_to_video.
adjust_video_speedAdjust video speed: Adjust video playback speed for fast-motion or slow-motion effects. For detailed input and output parameters, see - adjust_video_speed. + Adjust video speed: Change video playback speed for fast or slow motion effects. For detailed input and output parameters, see + adjust_video_speed.
concat_audioConcatenate audio: Concatenate multiple audio clips into a single audio file. For detailed input and output parameters, see - concat_audio. + Concatenate audio: Merge multiple audio clips into a single audio file. For detailed input and output parameters, see + concat_audio.
concat_videoConcatenate video: Concatenate multiple video clips and optionally apply transition effects. For detailed input and output parameters, see - concat_video. + Concatenate video: Merge multiple video clips into a new video with optional transitions. For detailed input and output parameters, see + concat_video.
extract_audioExtract audio: Extract the audio stream from a video as an independent audio file. For detailed input and output parameters, see - extract_audio. + Extract audio: Separate the audio stream from a video and save it as an independent audio file. For detailed input and output parameters, see + extract_audio.
flip_videoFlip video: Flip a video vertically or horizontally. For detailed input and output parameters, see - flip_video. + Flip video: Flip a video horizontally or vertically. For detailed input and output parameters, see + flip_video.
image_to_videoImage to video: Generate an animated video from multiple images with optional transitions. For detailed input and output parameters, see - image_to_video. + Image to video: Create an animated video from multiple images with optional transitions. For detailed input and output parameters, see + image_to_video.
mux_audio_videoMux audio and video: Combine an audio track and a video track into one video file. For detailed input and output parameters, see - mux_audio_video. + Mux audio and video: Combine a video track and an audio track into one video file. For detailed input and output parameters, see + mux_audio_video.
trim_audio Trim audio: Trim an audio file by start and end time. For detailed input and output parameters, see - trim_audio. + trim_audio.
trim_video Trim video: Trim a video by start and end time. For detailed input and output parameters, see - trim_video. + trim_video.
Video enhancement video erase_video_subtitle_proErase video subtitles: Remove subtitles or text from videos with high-quality restoration. For detailed input and output parameters, see - erase_video_subtitle_pro. + Erase video subtitles: Remove subtitles or text from a video with high-quality restoration. For detailed input and output parameters, see + erase_video_subtitle_pro.
enhance_videoEnhance video: Improve video quality for AIGC, UGC, short drama, education, game, and old film restoration scenarios. For detailed input and output parameters, see - enhance_video. + Enhance video: Improve video quality for AIGC, UGC, short drama, education, gaming, and old film restoration scenarios. For detailed input and output parameters, see + enhance_video.
-## Configuration +# Quick Start in Trae -| Variable | Description | Required | -| --- | --- | --- | -| `MEDIAKIT_API_KEY` | MediaKit API key used for authentication. | Yes | -| `MEDIAKIT_ENDPOINT` | MediaKit API endpoint. Defaults to `https://amk.cn-beijing.volces.com`. | No | -| `MCP_DOMAINS` | Comma-separated domain allowlist, such as `editing,video`. | No | -| `MCP_TOOLS` | Comma-separated tool allowlist, such as `trim_video,query_task`. | No | +Trae is an AI-native IDE with strong agent collaboration capabilities. By connecting MediaKit MCP, you can call cloud media processing capabilities in Trae with natural language and quickly complete tasks such as video editing, subtitle processing, audio processing, and video enhancement. -## Quick Start +## Prerequisites -Install the package: +- Prepare a valid MediaKit API key. +- Confirm the MediaKit service endpoint. If not explicitly configured, the default is `https://amk.cn-beijing.volces.com`. +- Install the [Trae client](https://www.trae.com.cn/). +- For local mode or self-hosted cloud mode, make sure `uvx` is installed in your local environment. Run `uvx --version` to check. If it is not installed, follow the [uv installation guide](https://docs.astral.sh/uv/getting-started/installation/). -```bash -pip install mcp-server-mediakit +## Steps + +### Step 1: Choose an Access Mode + +Choose one of the following modes based on your usage scenario: + +| Mode | Best for | Access method | +| --- | --- | --- | +| **Local Mode (JSON Local)** | Personal debugging, quick trials, no self-hosted service required. | Use `uvx` to launch MediaKit MCP directly from the `mcp-server` repository subdirectory. | +| **Cloud Mode (JSON URL)** | Team sharing, long-term usage, centralized operations. | Deploy MediaKit MCP Server yourself, then connect using the deployed Streamable HTTP endpoint. | + +### Step 2: Add MCP Configuration + +1. Open Trae and click the settings button in the top-right corner. +2. In the MCP tab, click **Add** > **Add Manually**. +3. Copy the JSON configuration for your selected mode and replace the fields as described below. + +#### Local Mode (JSON Local) + +Copy the following JSON and replace the fields as needed. Trae uses `uvx` to fetch the remote code and run it locally. + +```json +{ + "mcpServers": { + "mediakit_mcp": { + "command": "uvx", + "args": [ + "--from", + "git+https://github.com/volcengine/mcp-server.git#subdirectory=server/mcp_server_mediakit", + "mcp-server-mediakit" + ], + "env": { + "MEDIAKIT_API_KEY": "your-api-key", + "MEDIAKIT_ENDPOINT": "https://amk.cn-beijing.volces.com", + "MCP_DOMAINS": "editing,video" + } + } + } +} ``` -Set your MediaKit API key: +**Field replacement notes:** + +- `mediakit_mcp`: The MCP service name. You can customize it. +- `MEDIAKIT_API_KEY`: Replace with your MediaKit API key. +- `MEDIAKIT_ENDPOINT`: The MediaKit service endpoint. Keep `https://amk.cn-beijing.volces.com` if you use the default endpoint. +- `MCP_DOMAINS`: Load tools by group, for example `editing,video`. To load tools by exact tool name, use `MCP_TOOLS` instead. + +To load by tool name, use a configuration like this: + +```json +{ + "mcpServers": { + "mediakit_mcp": { + "command": "uvx", + "args": [ + "--from", + "git+https://github.com/volcengine/mcp-server.git#subdirectory=server/mcp_server_mediakit", + "mcp-server-mediakit" + ], + "env": { + "MEDIAKIT_API_KEY": "your-api-key", + "MEDIAKIT_ENDPOINT": "https://amk.cn-beijing.volces.com", + "MCP_TOOLS": "trim_video,query_task" + } + } + } +} +``` + +#### Cloud Mode (JSON URL) + +Cloud mode does not provide a prebuilt deployment URL. Before using this mode, you need to deploy MediaKit MCP Server yourself and make sure the service is reachable through Streamable HTTP. After deployment, record the service URL, for example `https://your-domain/mcp`, and then connect it in Trae as shown below. + +A simple startup example is: ```bash export MEDIAKIT_API_KEY="your-api-key" +export MEDIAKIT_ENDPOINT="https://amk.cn-beijing.volces.com" +export MCP_SERVER_HOST="0.0.0.0" +export MCP_SERVER_PORT="8000" +export STREAMABLE_HTTP_PATH="/mcp" + +uvx --from "git+https://github.com/volcengine/mcp-server.git#subdirectory=server/mcp_server_mediakit" mcp-server-mediakit --transport streamable-http ``` -Run with stdio transport: +After deployment, copy the following JSON and replace the fields as needed: + +```json +{ + "mcpServers": { + "mediakit_mcp": { + "url": "https://your-domain/mcp", + "headers": { + "x-amk-api-key": "your-api-key", + "x-mediakit-endpoint": "https://amk.cn-beijing.volces.com", + "x-mcp-domains": "editing,video" + } + } + } +} +``` -```bash -mcp-server-mediakit --transport stdio +**Field replacement notes:** + +- `mediakit_mcp`: The MCP service name. You can customize it. +- `url`: Replace with your self-hosted MediaKit MCP Streamable HTTP URL, such as `https://your-domain/mcp`. +- `x-amk-api-key`: Replace with your MediaKit API key. +- `x-mediakit-endpoint`: The MediaKit service endpoint. Use `https://amk.cn-beijing.volces.com` if you use the default endpoint. +- `x-mcp-domains`: Load tools by group, for example `editing,video`. To load tools by exact tool name, use `x-mcp-tools` instead. + +To load by tool name, use a configuration like this: + +```json +{ + "mcpServers": { + "mediakit_mcp": { + "url": "https://your-domain/mcp", + "headers": { + "x-amk-api-key": "your-api-key", + "x-mediakit-endpoint": "https://amk.cn-beijing.volces.com", + "x-mcp-tools": "trim_video,query_task" + } + } + } +} ``` -Run with streamable HTTP transport: +4. Make sure the MCP status is shown as active in Trae. -```bash -mcp-server-mediakit --transport streamable-http -``` +### Step 3: Start Agent Conversations + +Open the chat panel in Trae and switch the agent mode to one that supports MCP. Then you can directly issue natural language instructions such as: + +- Trim this video to the first 10 seconds and output a new video. +- Add Chinese subtitles to this video with font size 28. +- Remove the subtitles at the bottom of this video, then enhance the processed video quality. +- Concatenate these two audio files, and if the task is asynchronous, continue querying until the final result is ready. ## Usage Notes - Synchronous tasks return results directly. -- Asynchronous tasks return a `task_id`; call `query_task` to get task status and results. +- Asynchronous tasks return a `task_id`, and you need to call `query_task` to get task status and results. - Idempotency is enabled by default. Requests from the same account with the same core parameters within 2 days return the first task result instead of creating duplicate tasks. - To control idempotency explicitly, pass `client_token`. Reuse the same value for retries and use a new unique value to force a new task. +- `client_token` is generated by the client and must not exceed 64 characters. + +## MCP Configuration Reference + +The table below lists the core MediaKit MCP configuration fields for cloud mode and local mode. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Cloud headerLocal environment variableExampleDescription
x-amk-api-keyMEDIAKIT_API_KEYyour-api-keyMediaKit API key used for authentication.
x-mediakit-endpointMEDIAKIT_ENDPOINThttps://amk.cn-beijing.volces.comMediaKit service endpoint. The default value is this endpoint.
x-mcp-domainsMCP_DOMAINSediting,videoLoad tools by group. Separate multiple groups with commas.
x-mcp-toolsMCP_TOOLStrim_video,query_taskLoad tools by tool name. Separate multiple tool names with commas.
+ +For self-hosted cloud mode, you can also configure the following startup parameters: + +| Environment variable | Default value | Description | +| --- | --- | --- | +| `MCP_SERVER_HOST` | `0.0.0.0` | MCP service bind address. | +| `MCP_SERVER_PORT` | `8000` | MCP service listening port. | +| `STREAMABLE_HTTP_PATH` | `/mcp` | Streamable HTTP path. | ## Tool Details @@ -166,11 +331,11 @@ Query asynchronous task status. Supports one-shot query or polling through `poll ### add_image_to_video -Add an image overlay to a video. Commonly used for image watermarks. Supports image size, position, start time, and end time. +Add an image overlay to a video. Commonly used for image watermarks. Supports image width, height, horizontal position, vertical position, start time, and end time. ### add_subtitle_to_video -Burn subtitle files or structured subtitle text into a video. Supports subtitle position, font size, font color, and font type. +Burn subtitle files or subtitle text into a video. Supports subtitle position, font size, font color, and font type. ### adjust_video_speed @@ -178,27 +343,27 @@ Adjust video playback speed. Supports speed values from `0.1` to `4`. ### concat_audio -Concatenate audio clips. Supports up to 100 audio URLs. +Concatenate multiple audio clips. Supports up to 100 audio URLs. ### concat_video -Concatenate video clips. Supports up to 100 video URLs and optional transition effects. +Concatenate multiple video clips. Supports up to 100 video URLs and optional transition effects. ### extract_audio -Extract audio from a video and output `mp3` or `m4a`. +Extract audio from a video. Supports `mp3` or `m4a` output. ### flip_video -Flip a video vertically or horizontally. +Flip video frames horizontally or vertically. ### image_to_video -Generate an animated video from multiple images and optional transition effects. +Create an animated video from multiple images and optional transition effects. ### mux_audio_video -Combine a video and an audio file. Supports preserving the original video audio and synchronizing audio/video duration. +Combine a video and an audio file into one video. Supports preserving the original video audio and synchronizing duration by video or audio timeline. ### trim_audio @@ -210,8 +375,12 @@ Trim a video by start and end time in seconds. ### erase_video_subtitle_pro -Remove subtitles or text from videos and restore the visual content. Supports mainstream video formats such as `mp4`, `flv`, `ts`, `avi`, `mov`, `wmv`, and `mkv`. +Remove subtitles or text from a video with high-quality restoration. Supports mainstream video formats such as `mp4`, `flv`, `ts`, `avi`, `mov`, `wmv`, and `mkv`. ### enhance_video -Enhance video quality for scenarios such as `common`, `ugc`, `short_series`, `aigc`, and `old_film`. Supports standard and professional tool versions. +Enhance video quality for scenarios such as `common`, `ugc`, `short_series`, `aigc`, and `old_film`. Supports both standard and professional versions. + +## License + +MIT diff --git a/server/mcp_server_mediakit/README_zh.md b/server/mcp_server_mediakit/README_zh.md index 8d936cbf..af723844 100644 --- a/server/mcp_server_mediakit/README_zh.md +++ b/server/mcp_server_mediakit/README_zh.md @@ -28,7 +28,7 @@ MediaKit MCP 已开放的能力覆盖了从异步任务查询到深度媒体编 shared query_task 任务查询:查询异步任务状态和结果。提交异步任务后,使用该工具获取处理进度和最终产物。详细输入和输出参数请见 - query_task。 + query_task。 @@ -36,67 +36,67 @@ MediaKit MCP 已开放的能力覆盖了从异步任务查询到深度媒体编 editing add_image_to_video 视频加图片:在视频画面上叠加图片,常用于添加图片水印。详细输入和输出参数请见 - add_image_to_video。 + add_image_to_video。 add_subtitle_to_video 视频加字幕:将字幕文件或字幕文本压制到视频画面中。详细输入和输出参数请见 - add_subtitle_to_video。 + add_subtitle_to_video。 adjust_video_speed 视频播放调速:调整视频播放速度,实现快放或慢放效果。详细输入和输出参数请见 - adjust_video_speed。 + adjust_video_speed。 concat_audio 音频拼接:将多个音频片段拼接为一个新的音频文件。详细输入和输出参数请见 - concat_audio。 + concat_audio。 concat_video 视频拼接:将多个视频片段拼接为一个新视频,支持添加转场效果。详细输入和输出参数请见 - concat_video。 + concat_video。 extract_audio 音频提取:从视频文件中分离音频流,并保存为独立音频文件。详细输入和输出参数请见 - extract_audio。 + extract_audio。 flip_video 画面翻转:对视频画面进行水平或垂直镜像翻转。详细输入和输出参数请见 - flip_video。 + flip_video。 image_to_video 图片合成视频:将多张图片合成为动画视频,支持转场效果。详细输入和输出参数请见 - image_to_video。 + image_to_video。 mux_audio_video 音画合成:将视频轨道与音频轨道合成一个视频文件。详细输入和输出参数请见 - mux_audio_video。 + mux_audio_video。 trim_audio 音频裁剪:按起止时间点裁剪音频,生成新的音频片段。详细输入和输出参数请见 - trim_audio。 + trim_audio。 trim_video 视频裁剪:按起止时间点裁剪视频,生成新的视频片段。详细输入和输出参数请见 - trim_video。 + trim_video。 @@ -104,52 +104,167 @@ MediaKit MCP 已开放的能力覆盖了从异步任务查询到深度媒体编 video erase_video_subtitle_pro 视频字幕擦除:针对视频中的字幕或文本进行高质量无痕擦除。详细输入和输出参数请见 - erase_video_subtitle_pro。 + erase_video_subtitle_pro。 enhance_video 画质增强:面向 AIGC、UGC、短剧、教育、游戏、老片修复等场景提升视频画质。详细输入和输出参数请见 - enhance_video。 + enhance_video。 -## 配置说明 +# 快速体验:在 Trae 中配置 -| 变量 | 说明 | 是否必填 | -| --- | --- | --- | -| `MEDIAKIT_API_KEY` | MediaKit API Key,用于请求鉴权。 | 是 | -| `MEDIAKIT_ENDPOINT` | MediaKit API Endpoint,默认 `https://amk.cn-beijing.volces.com`。 | 否 | -| `MCP_DOMAINS` | 工具分组白名单,多个分组用英文逗号分隔,例如 `editing,video`。 | 否 | -| `MCP_TOOLS` | 工具名白名单,多个工具用英文逗号分隔,例如 `trim_video,query_task`。 | 否 | +Trae 是一款 AI 原生 IDE,提供了强大的智能体协作能力。通过接入 MediaKit MCP,您可以直接在 Trae 对话框中以自然语言方式调用云端媒体处理能力,快速完成视频剪辑、字幕处理、音频处理和画质增强等任务。 -## 快速开始 +## 前提条件 -安装 MCP Server: +- 已准备可用的 MediaKit API Key。 +- 已确认 MediaKit 服务接入地址。若未显式配置,默认使用 `https://amk.cn-beijing.volces.com`。 +- 已安装 [Trae 客户端](https://www.trae.com.cn/)。 +- 使用本地模式或云端自部署模式时,需确保本地开发环境已安装 `uvx`。可通过 `uvx --version` 检查;若提示未安装,请参考 [uv 官方安装文档](https://docs.astral.sh/uv/getting-started/installation/)。 -```bash -pip install mcp-server-mediakit +## 操作步骤 + +### 步骤 1:选择接入模式 + +根据您的使用场景,选择以下两种接入模式之一: + +| 模式 | 适用场景 | 接入方式 | +| --- | --- | --- | +| **本地模式(JSON Local)** | 个人调试、快速试用、无需自建服务。 | 通过 `uvx` 直接从 `mcp-server` 仓库子目录拉起 MediaKit MCP。 | +| **云端模式(JSON URL)** | 团队共享、长期稳定使用、统一运维。 | 先自行部署 MediaKit MCP Server,再使用部署后的 Streamable HTTP 地址接入。 | + +### 步骤 2:添加 MCP 配置 + +1. 打开 Trae,单击窗口右上角“设置”按钮。 +2. 在 MCP 页签下,单击**添加** > **手动添加**。 +3. 根据您在步骤 1 中选择的模式,复制对应 JSON 配置,并按下方说明替换参数。 + +#### 本地模式(JSON Local) + +复制以下 JSON 并根据下方文字说明进行替换。Trae 会通过 `uvx` 自动拉取远程代码并在本地运行。 + +```json +{ + "mcpServers": { + "mediakit_mcp": { + "command": "uvx", + "args": [ + "--from", + "git+https://github.com/volcengine/mcp-server.git#subdirectory=server/mcp_server_mediakit", + "mcp-server-mediakit" + ], + "env": { + "MEDIAKIT_API_KEY": "your-api-key", + "MEDIAKIT_ENDPOINT": "https://amk.cn-beijing.volces.com", + "MCP_DOMAINS": "editing,video" + } + } + } +} ``` -配置 MediaKit API Key: +**字段替换说明:** + +- `mediakit_mcp`:MCP 服务名称,您可以根据需要自定义。 +- `MEDIAKIT_API_KEY`:请替换为您的 MediaKit API Key。 +- `MEDIAKIT_ENDPOINT`:MediaKit 服务地址。若使用默认接入地址,可保留 `https://amk.cn-beijing.volces.com`。 +- `MCP_DOMAINS`:按分组加载工具,例如 `editing,video`。如需按工具名精确加载,可改用 `MCP_TOOLS`。 + +如需按工具名加载,可参考以下写法: + +```json +{ + "mcpServers": { + "mediakit_mcp": { + "command": "uvx", + "args": [ + "--from", + "git+https://github.com/volcengine/mcp-server.git#subdirectory=server/mcp_server_mediakit", + "mcp-server-mediakit" + ], + "env": { + "MEDIAKIT_API_KEY": "your-api-key", + "MEDIAKIT_ENDPOINT": "https://amk.cn-beijing.volces.com", + "MCP_TOOLS": "trim_video,query_task" + } + } + } +} +``` + +#### 云端模式(JSON URL) + +云端模式不提供现成的部署链接。使用该模式前,您需要先自行部署 MediaKit MCP Server,并确保服务可通过 Streamable HTTP 方式访问。部署完成后,请记录可访问的服务地址,例如 `https://your-domain/mcp`,然后在 Trae 中按如下方式接入。 + +一种简单的启动示例如下: ```bash export MEDIAKIT_API_KEY="your-api-key" +export MEDIAKIT_ENDPOINT="https://amk.cn-beijing.volces.com" +export MCP_SERVER_HOST="0.0.0.0" +export MCP_SERVER_PORT="8000" +export STREAMABLE_HTTP_PATH="/mcp" + +uvx --from "git+https://github.com/volcengine/mcp-server.git#subdirectory=server/mcp_server_mediakit" mcp-server-mediakit --transport streamable-http ``` -以 stdio 模式启动: +完成部署后,复制以下 JSON 并根据下方文字说明进行替换: + +```json +{ + "mcpServers": { + "mediakit_mcp": { + "url": "https://your-domain/mcp", + "headers": { + "x-amk-api-key": "your-api-key", + "x-mediakit-endpoint": "https://amk.cn-beijing.volces.com", + "x-mcp-domains": "editing,video" + } + } + } +} +``` -```bash -mcp-server-mediakit --transport stdio +**字段替换说明:** + +- `mediakit_mcp`:MCP 服务名称,您可以根据需要自定义。 +- `url`:请替换为您自行部署后的 MediaKit MCP Streamable HTTP 地址,例如 `https://your-domain/mcp`。 +- `x-amk-api-key`:请替换为您的 MediaKit API Key。 +- `x-mediakit-endpoint`:MediaKit 服务地址。若使用默认接入地址,可填写 `https://amk.cn-beijing.volces.com`。 +- `x-mcp-domains`:按分组加载工具,例如 `editing,video`。如需按工具名精确加载,可改用 `x-mcp-tools`。 + +如需按工具名加载,可参考以下写法: + +```json +{ + "mcpServers": { + "mediakit_mcp": { + "url": "https://your-domain/mcp", + "headers": { + "x-amk-api-key": "your-api-key", + "x-mediakit-endpoint": "https://amk.cn-beijing.volces.com", + "x-mcp-tools": "trim_video,query_task" + } + } + } +} ``` -以 Streamable HTTP 模式启动: +4. 确认该 MCP 的状态显示为绿色激活。 -```bash -mcp-server-mediakit --transport streamable-http -``` +### 步骤 3:启用智能体对话 + +在 Trae 主界面打开对话面板,将底部智能体切换为支持 MCP 的模式。随后,您可以直接下达自然语言指令,例如: + +- 帮我把这个视频裁剪为前 10 秒,并输出一个新视频。 +- 帮我给这个视频添加中文字幕,字号设置为 28。 +- 帮我擦除视频底部字幕,并对处理后的视频做画质增强。 +- 帮我把两段音频拼接起来,如果任务是异步的,请继续帮我查询最终结果。 ## 使用说明 @@ -159,6 +274,55 @@ mcp-server-mediakit --transport streamable-http - 如需主动控制幂等,可传 `client_token`;请求重试时复用同一值,强制重新执行时必须传新的唯一值。 - `client_token` 由客户端生成,长度不超过 64 个字符。 +## MCP 配置参数说明 + +下表列出 MediaKit MCP 的核心配置项,区分云端模式与本地模式。请根据您的实际接入场景选择对应字段。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
云端 Header 字段本地环境变量名示例说明
x-amk-api-keyMEDIAKIT_API_KEYyour-api-keyMediaKit API Key,用于请求鉴权。
x-mediakit-endpointMEDIAKIT_ENDPOINThttps://amk.cn-beijing.volces.comMediaKit 服务地址;未配置时默认使用该地址。
x-mcp-domainsMCP_DOMAINSediting,video按工具分组加载,多个分组用英文逗号分隔。
x-mcp-toolsMCP_TOOLStrim_video,query_task按工具名加载,多个工具名用英文逗号分隔。
+ +云端自部署时,还可按需使用以下服务启动参数: + +| 环境变量 | 默认值 | 说明 | +| --- | --- | --- | +| `MCP_SERVER_HOST` | `0.0.0.0` | MCP 服务监听地址。 | +| `MCP_SERVER_PORT` | `8000` | MCP 服务监听端口。 | +| `STREAMABLE_HTTP_PATH` | `/mcp` | Streamable HTTP 路径。 | + ## 工具详情 ### query_task @@ -216,3 +380,7 @@ mcp-server-mediakit --transport streamable-http ### enhance_video 针对 `common`、`ugc`、`short_series`、`aigc`、`old_film` 等场景进行画质增强,支持标准版和专业版工具版本。 + +## License + +MIT From f4c16f3d1316023fc9df41d16839860f8a14fd3d Mon Sep 17 00:00:00 2001 From: tiehongji Date: Mon, 22 Jun 2026 21:11:09 +0800 Subject: [PATCH 5/6] y --- .../mcp_server_mediakit/src/base/api_info.py | 25 + .../src/mediakit/mcp_tools/audio.py | 68 ++ .../src/mediakit/mcp_tools/editing.py | 103 ++- .../src/mediakit/mcp_tools/image.py | 126 ++++ .../src/mediakit/mcp_tools/video.py | 239 ++++++- .../src/mediakit/utils/response.py | 37 ++ server/mcp_server_mediakit/tools.json | 626 ++++++++++++++++++ server/mcp_server_mediakit/trae.json | 107 +++ 8 files changed, 1326 insertions(+), 5 deletions(-) create mode 100644 server/mcp_server_mediakit/src/mediakit/mcp_tools/audio.py create mode 100644 server/mcp_server_mediakit/src/mediakit/mcp_tools/image.py create mode 100644 server/mcp_server_mediakit/tools.json create mode 100644 server/mcp_server_mediakit/trae.json diff --git a/server/mcp_server_mediakit/src/base/api_info.py b/server/mcp_server_mediakit/src/base/api_info.py index d588388d..3d41341a 100644 --- a/server/mcp_server_mediakit/src/base/api_info.py +++ b/server/mcp_server_mediakit/src/base/api_info.py @@ -1,16 +1,41 @@ api_info = { "add_image_to_video": {"path": "/api/v1/tools/add-image-to-video", "method": "POST"}, "add_subtitle_to_video": {"path": "/api/v1/tools/add-subtitle-to-video", "method": "POST"}, + "adjust_audio_speed": {"path": "/api/v1/tools/adjust-audio-speed", "method": "POST"}, "adjust_video_speed": {"path": "/api/v1/tools/adjust-video-speed", "method": "POST"}, + "adjust_video_volume": {"path": "/api/v1/tools/adjust-video-volume", "method": "POST"}, + "analyze_video_highlights": {"path": "/api/v1/tools/analyze-video-highlights", "method": "POST"}, + "analyze_video_storyline": {"path": "/api/v1/tools/analyze-video-storyline", "method": "POST"}, + "apply_video_filter": {"path": "/api/v1/tools/apply-video-filter", "method": "POST"}, + "asr_subtitles": {"path": "/api/v1/tools/asr-subtitles", "method": "POST"}, "concat_audio": {"path": "/api/v1/tools/concat-audio", "method": "POST"}, "concat_video": {"path": "/api/v1/tools/concat-video", "method": "POST"}, + "enhance_image": {"path": "/api/v1/tools-sync/enhance-image", "method": "POST"}, "enhance_video": {"path": "/api/v1/tools/enhance-video", "method": "POST"}, + "enhance_video_generative": {"path": "/api/v1/tools/enhance-video-generative", "method": "POST"}, + "erase_image": {"path": "/api/v1/tools-sync/erase-image", "method": "POST"}, + "erase_video_subtitle": {"path": "/api/v1/tools/erase-video-subtitle", "method": "POST"}, "erase_video_subtitle_pro": {"path": "/api/v1/tools/erase-video-subtitle-pro", "method": "POST"}, + "evaluate_image_quality": {"path": "/api/v1/tools-sync/evaluate-image-quality", "method": "POST"}, "extract_audio": {"path": "/api/v1/tools/extract-audio", "method": "POST"}, + "fade_audio": {"path": "/api/v1/tools/fade-audio", "method": "POST"}, + "fade_video_audio": {"path": "/api/v1/tools/fade-video-audio", "method": "POST"}, "flip_video": {"path": "/api/v1/tools/flip-video", "method": "POST"}, + "generate_highlights_microdrama": {"path": "/api/v1/tools/generate-highlights-microdrama", "method": "POST"}, + "generate_highlights_minigame": {"path": "/api/v1/tools/generate-highlights-minigame", "method": "POST"}, + "image_ocr": {"path": "/api/v1/tools-sync/image-ocr", "method": "POST"}, "image_to_video": {"path": "/api/v1/tools/image-to-video", "method": "POST"}, + "matte_greenscreen_video": {"path": "/api/v1/tools/matte-greenscreen-video", "method": "POST"}, + "matte_portrait_video": {"path": "/api/v1/tools/matte-portrait-video", "method": "POST"}, + "mix_audio": {"path": "/api/v1/tools/mix-audio", "method": "POST"}, "mux_audio_video": {"path": "/api/v1/tools/mux-audio-video", "method": "POST"}, + "probe_audio_metadata": {"path": "/api/v1/tools/probe-audio-metadata", "method": "POST"}, + "probe_video_metadata": {"path": "/api/v1/tools/probe-video-metadata", "method": "POST"}, "query_task": {"path": "/api/v1/tasks/{task_id}", "method": "GET"}, + "remove_image_background": {"path": "/api/v1/tools-sync/remove-image-background", "method": "POST"}, + "segment_scenes": {"path": "/api/v1/tools/segment-scenes", "method": "POST"}, + "separate_voice": {"path": "/api/v1/tools/separate-voice", "method": "POST"}, "trim_audio": {"path": "/api/v1/tools/trim-audio", "method": "POST"}, "trim_video": {"path": "/api/v1/tools/trim-video", "method": "POST"}, + "video_ocr": {"path": "/api/v1/tools/video-ocr", "method": "POST"}, } diff --git a/server/mcp_server_mediakit/src/mediakit/mcp_tools/audio.py b/server/mcp_server_mediakit/src/mediakit/mcp_tools/audio.py new file mode 100644 index 00000000..bd933551 --- /dev/null +++ b/server/mcp_server_mediakit/src/mediakit/mcp_tools/audio.py @@ -0,0 +1,68 @@ +from __future__ import annotations + +from typing import Any, Dict, List, Optional +from typing_extensions import NotRequired, Required, TypedDict + +try: + from pydantic import Field +except Exception: # pragma: no cover + def Field(*args, **kwargs): + if args: + return args[0] + return kwargs.get("default", None) + +try: + from mcp.server.fastmcp.server import Context + from mcp.server.session import ServerSession +except Exception: # pragma: no cover + class Context: # type: ignore + pass + + class ServerSession: # type: ignore + pass + +from base.client import MediKitClient +from ..utils.response import async_task_response, error_response + +TOOL_NAMES = ['probe_audio_metadata', 'separate_voice'] + + +def register_tools(mcp, client: MediKitClient) -> None: + @mcp.tool(name="separate_voice", description="将音频中的人声与背景音精准分离,输出为两个独立的音轨文件。\n支持格式:主流音视频格式(如mp4、mov、mp3、m4a、wav等)。\n输入:video_url和audio_url二选一。\n输出格式:AAC。 使用 task_id, 调用 query_task 方法获取结果") + async def separate_voice( + video_url: Optional[str] = Field(None, description="输入视频 Url(需公网可访问),与audio_url二选一,都存在时优先取video_url"), + audio_url: Optional[str] = Field(None, description="输入音频 Url(需公网可访问),与video_url二选一,不能都为空"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """将音频中的人声与背景音精准分离,输出为两个独立的音轨文件。 +支持格式:主流音视频格式(如mp4、mov、mp3、m4a、wav等)。 +输入:video_url和audio_url二选一。 +输出格式:AAC。""" + try: + result = client.call(api_name="separate_voice", video_url=video_url, audio_url=audio_url, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="probe_audio_metadata", description="获取指定音频的详细元信息,输出容器层信息(format_meta)与音频流元信息(audio_stream_meta)。\n字段分类参考 ffprobe,并对 VOD 原始返回做精简与统一。\n使用限制:支持公网 HTTP/HTTPS URL。 使用 task_id, 调用 query_task 方法获取结果") + async def probe_audio_metadata( + audio_url: str = Field(..., description="待探测的音频公网 HTTP/HTTPS URL。"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """获取指定音频的详细元信息,输出容器层信息(format_meta)与音频流元信息(audio_stream_meta)。 +字段分类参考 ffprobe,并对 VOD 原始返回做精简与统一。 +使用限制:支持公网 HTTP/HTTPS URL。""" + try: + result = client.call(api_name="probe_audio_metadata", audio_url=audio_url, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + if hasattr(mcp, "register_domain_tools"): + mcp.register_domain_tools("audio", TOOL_NAMES) diff --git a/server/mcp_server_mediakit/src/mediakit/mcp_tools/editing.py b/server/mcp_server_mediakit/src/mediakit/mcp_tools/editing.py index 9f3be50e..81bc5788 100644 --- a/server/mcp_server_mediakit/src/mediakit/mcp_tools/editing.py +++ b/server/mcp_server_mediakit/src/mediakit/mcp_tools/editing.py @@ -36,7 +36,7 @@ class ImageToVideoImagesItem(TypedDict): animation_in: NotRequired[float] animation_out: NotRequired[float] -TOOL_NAMES = ['add_image_to_video', 'add_subtitle_to_video', 'adjust_video_speed', 'concat_audio', 'concat_video', 'extract_audio', 'flip_video', 'image_to_video', 'mux_audio_video', 'trim_audio', 'trim_video'] +TOOL_NAMES = ['add_image_to_video', 'add_subtitle_to_video', 'adjust_audio_speed', 'adjust_video_speed', 'adjust_video_volume', 'apply_video_filter', 'concat_audio', 'concat_video', 'extract_audio', 'fade_audio', 'fade_video_audio', 'flip_video', 'image_to_video', 'mix_audio', 'mux_audio_video', 'trim_audio', 'trim_video'] def register_tools(mcp, client: MediKitClient) -> None: @@ -233,5 +233,106 @@ async def trim_video( except Exception as exc: return error_response(str(exc)) + @mcp.tool(name="adjust_audio_speed", description="调整音频的播放倍速,实现快放或慢放效果。 使用 task_id, 调用 query_task 方法获取结果") + async def adjust_audio_speed( + audio_url: str = Field(..., description="输入音频。支持http://xxx或https://xxx格式 Url,支持 mp3、m4a、wav 等格式"), + speed: Optional[float] = Field(1, description="调整速度的倍数,Float类型,取值范围为0.1~4。0.1=放慢至原速的 0.1 倍,1=原速,4=加速至原速的 4 倍。"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """调整音频的播放倍速,实现快放或慢放效果。""" + try: + result = client.call(api_name="adjust_audio_speed", audio_url=audio_url, speed=speed, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="adjust_video_volume", description="调整视频音量大小,支持静音;输出 mp4,分辨率与原片一致。 使用 task_id, 调用 query_task 方法获取结果") + async def adjust_video_volume( + video_url: str = Field(..., description="输入视频。支持http://xxx或https://xxx格式 URL,支持 mp4、mov、flv、ts、avi、wmv、mkv 等格式,最高 4K"), + volume: Optional[float] = Field(1, description="音量倍数。Float 类型,取值范围 0~4。0=静音,1=原音量,4=放大 4 倍。"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """调整视频音量大小,支持静音;输出 mp4,分辨率与原片一致。""" + try: + result = client.call(api_name="adjust_video_volume", video_url=video_url, volume=volume, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="apply_video_filter", description="为视频添加指定滤镜效果,输出mp4,分辨率与原片一致。 使用 task_id, 调用 query_task 方法获取结果") + async def apply_video_filter( + video_url: str = Field(..., description="输入视频。支持http://xxx或https://xxx格式 URL,支持 mp4、mov、flv、ts、avi、wmv、mkv 等格式,最高 4K"), + filter_style: Optional[str] = Field('spring', description="滤镜风格。根据用户想要的视频画面效果选择:\n- spring:春日滤镜\n- sunset:晚霞滤镜\n- vivid:鲜亮滤镜\n- fair_skin:白皙滤镜\n- food:食物滤镜\n"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """为视频添加指定滤镜效果,输出mp4,分辨率与原片一致。""" + try: + result = client.call(api_name="apply_video_filter", video_url=video_url, filter_style=filter_style, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="fade_audio", description="对输入音频实现淡入淡出效果,输出 mp3。 使用 task_id, 调用 query_task 方法获取结果") + async def fade_audio( + audio_url: str = Field(..., description="输入音频。支持http://xxx或https://xxx格式 URL,支持 mp3、m4a、wav、flac 等格式"), + fade_in_duration: Optional[float] = Field(1, description="声音淡入时长。单位:秒,可传小数(最多3位小数)。0 表示不淡入。"), + fade_out_duration: Optional[float] = Field(1, description="声音淡出时长。单位:秒,可传小数(最多3位小数)。0 表示不淡出。"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """对输入音频实现淡入淡出效果,输出 mp3。""" + try: + result = client.call(api_name="fade_audio", audio_url=audio_url, fade_in_duration=fade_in_duration, fade_out_duration=fade_out_duration, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="fade_video_audio", description="对输入视频的声轨实现淡入淡出效果。\n输出 mp4,分辨率与原片一致。 使用 task_id, 调用 query_task 方法获取结果") + async def fade_video_audio( + video_url: str = Field(..., description="输入视频。支持http://xxx或https://xxx格式 URL,支持 mp4、mov、flv、ts、avi、wmv、mkv 等格式,最高 4K"), + fade_in_duration: Optional[float] = Field(1, description="声音淡入时长。单位:秒,可传小数(最多3位小数)。0 表示不淡入。"), + fade_out_duration: Optional[float] = Field(1, description="声音淡出时长。单位:秒,可传小数(最多3位小数)。0 表示不淡出。"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """对输入视频的声轨实现淡入淡出效果。 +输出 mp4,分辨率与原片一致。""" + try: + result = client.call(api_name="fade_video_audio", video_url=video_url, fade_in_duration=fade_in_duration, fade_out_duration=fade_out_duration, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="mix_audio", description="将多个音频文件(如背景音乐、音效、人声)进行混音,生成一个新的音频文件。\n处理耗时:处理耗时与视频时长正相关。视频时长越长,处理耗时越长。平均 RTF(处理耗时/原片时长)为 1。\n输出音频的时长以最长的音频为准。\n输出视频格式:mp3 使用 task_id, 调用 query_task 方法获取结果") + async def mix_audio( + audio_urls: List[str] = Field(..., description="待混合的音频列表,Array类型。最少传入1个,最多传入100个。\n子项说明:待混合的输入音频。支持http://xxx或https://xxx格式 URL,支持 mp3、wav、flac 等格式"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """将多个音频文件(如背景音乐、音效、人声)进行混音,生成一个新的音频文件。 +处理耗时:处理耗时与视频时长正相关。视频时长越长,处理耗时越长。平均 RTF(处理耗时/原片时长)为 1。 +输出音频的时长以最长的音频为准。 +输出视频格式:mp3""" + try: + result = client.call(api_name="mix_audio", audio_urls=audio_urls, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + if hasattr(mcp, "register_domain_tools"): mcp.register_domain_tools("editing", TOOL_NAMES) diff --git a/server/mcp_server_mediakit/src/mediakit/mcp_tools/image.py b/server/mcp_server_mediakit/src/mediakit/mcp_tools/image.py new file mode 100644 index 00000000..9d552a63 --- /dev/null +++ b/server/mcp_server_mediakit/src/mediakit/mcp_tools/image.py @@ -0,0 +1,126 @@ +from __future__ import annotations + +from typing import Any, Dict, List, Optional +from typing_extensions import NotRequired, Required, TypedDict + +try: + from pydantic import Field +except Exception: # pragma: no cover + def Field(*args, **kwargs): + if args: + return args[0] + return kwargs.get("default", None) + +try: + from mcp.server.fastmcp.server import Context + from mcp.server.session import ServerSession +except Exception: # pragma: no cover + class Context: # type: ignore + pass + + class ServerSession: # type: ignore + pass + +from base.client import MediKitClient +from ..utils.response import sync_result_response, error_response + +TOOL_NAMES = ['enhance_image', 'erase_image', 'evaluate_image_quality', 'image_ocr', 'remove_image_background'] + + +def register_tools(mcp, client: MediKitClient) -> None: + @mcp.tool(name="image_ocr", description="识别图片中的通用印刷体文字,返回可编辑文本、文字框坐标和置信度。\n本期支持简体中文和英文通用场景识别。") + async def image_ocr( + image_url: str = Field(..., description="输入图片 URL,需为公网可访问的 png/jpg/jpeg/webp/heic/avif 图片,单图不超过 10MB。"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """识别图片中的通用印刷体文字,返回可编辑文本、文字框坐标和置信度。 +本期支持简体中文和英文通用场景识别。""" + try: + result = client.call(api_name="image_ocr", image_url=image_url, callback_args=callback_args, client_token=client_token) + return sync_result_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="erase_image", description="自动检测并擦除图片中的常见图标、文字或指定区域内容,并对擦除区域进行背景智能填充。") + async def erase_image( + image_url: str = Field(..., description="输入图片 URL,需为公网可访问的 png/jpg/jpeg/webp/tiff/bmp/heic 图片,单图不超过 10MB。"), + tool_version: Optional[str] = Field('standard', description="图像擦除修复选用的模型版本。- standard:标准版。基于明确的规则(如文本匹配、矩形框坐标)擦除指定内容。适用于简单、明确的擦除任务。默认 standard。"), + standard_scene: Optional[str] = Field('full_screen_text_erase', description="标准版擦除场景,仅 standard 版本生效。full_screen_text_erase:全屏文字擦除,可通过standard_erase_text字段指定要擦除的文字,不指定则默认擦除所有文字内容。full_screen_icon_erase:全屏图标擦除。"), + standard_erase_text: Optional[str] = Field(None, description="标准版文字擦除,指定要擦除的文字,不指定则默认擦除所有文字内容。"), + output_format: Optional[str] = Field('webp', description="输出图片格式;默认 webp。"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """自动检测并擦除图片中的常见图标、文字或指定区域内容,并对擦除区域进行背景智能填充。""" + try: + result = client.call(api_name="erase_image", image_url=image_url, tool_version=tool_version, standard_scene=standard_scene, standard_erase_text=standard_erase_text, output_format=output_format, callback_args=callback_args, client_token=client_token) + return sync_result_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="remove_image_background", description="自动识别并保留图像主体,移除背景并生成透明背景图片。\n支持通用、人像、商品场景,可在人像/商品场景中生成主体描边或裁剪透明背景。") + async def remove_image_background( + image_url: str = Field(..., description="输入图片 URL,需为公网可访问的 png/jpg/jpeg/webp/tiff/bmp/ico 图片,单图不超过 10MB。"), + scene: str = Field(..., description="背景移除场景:general 为通用场景,适用于期望抠出图像主体但不确定该主体所属分类的场景。human 为人像抠图场景,适用于仅需抠出图像中的人像主体的场景,product 为商品抠图场景,适用于仅需抠出图像中的商品主体的场景。"), + need_contour: Optional[bool] = Field(False, description="是否为主体生成描边;默认 false,仅 human/product 场景生效,general 场景忽略。"), + contour_color: Optional[str] = Field('#FFFFFF', description="主体描边颜色,十六进制 RGB;默认 #FFFFFF,仅 need_contour=true 且 human/product 场景生效。"), + contour_size: Optional[int] = Field(10, description="主体描边宽度,单位 px;默认 10,仅 need_contour=true 且 human/product 场景生效。"), + need_crop_background: Optional[bool] = Field(False, description="是否裁剪透明背景到刚好包住主体;默认 false,仅 human/product 场景生效,general 场景忽略。"), + output_format: Optional[str] = Field('png', description="输出图片格式;默认 png。"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """自动识别并保留图像主体,移除背景并生成透明背景图片。 +支持通用、人像、商品场景,可在人像/商品场景中生成主体描边或裁剪透明背景。""" + try: + result = client.call(api_name="remove_image_background", image_url=image_url, scene=scene, need_contour=need_contour, contour_color=contour_color, contour_size=contour_size, need_crop_background=need_crop_background, output_format=output_format, callback_args=callback_args, client_token=client_token) + return sync_result_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="enhance_image", description="基于图像内容理解智能决策,全方位提升图片分辨率、清晰度与色彩表现。") + async def enhance_image( + image_url: str = Field(..., description="输入图片。String 类型,支持http://xxx或https://xxx格式 URL"), + tool_version: Optional[str] = Field('standard', description="画质增强选用的模型版本,标准版:standard;专业版:professional。默认为标准版"), + multiple: Optional[float] = Field(None, description="图像处理后较原图的分辨率倍数,支持 2 位小数。取值最大不超过 30,取值范围[1,30]。注意:图像处理后的宽度和高度不能超过target_width、target_height的上限值。standard模式下,取值最大不超过 8。"), + target_width: Optional[int] = Field(None, description="图像处理后的宽度,单位为 px,取值不能超过 10240。注意:standard模式下,取值最大不超过 6144,且图像处理后较原图的分辨率倍数不能超过 8。"), + target_height: Optional[int] = Field(None, description="图像处理后的高度,单位为 px,取值不能超过 10240。注意:standard模式下,取值最大不超过 6144,且图像处理后较原图的分辨率倍数不能超过 8。"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """基于图像内容理解智能决策,全方位提升图片分辨率、清晰度与色彩表现。""" + try: + result = client.call(api_name="enhance_image", image_url=image_url, tool_version=tool_version, multiple=multiple, target_width=target_width, target_height=target_height, callback_args=callback_args, client_token=client_token) + return sync_result_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="evaluate_image_quality", description="对输入图片进行主客观画质和美学评分,适用于质量监控、低质图筛查、内容审核、推荐排序和训练数据清洗等场景。\n支持标准版多维评分与专业版大模型评分。") + async def evaluate_image_quality( + image_url: str = Field(..., description="输入图片 URL,需为公网可访问的 png/jpeg/webp/heic 图片,单图不超过 10MB。"), + tool_version: Optional[str] = Field('standard', description="画质评估模型版本,standard 为标准版,professional 为专业版;默认 standard。"), + standard_evaluate_items: Optional[List[str]] = Field(['vqscore', 'noise', 'aesthetic', 'blur'], description="标准版选用的评估工具\n子项说明:评估工具。"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """对输入图片进行主客观画质和美学评分,适用于质量监控、低质图筛查、内容审核、推荐排序和训练数据清洗等场景。 +支持标准版多维评分与专业版大模型评分。""" + try: + result = client.call(api_name="evaluate_image_quality", image_url=image_url, tool_version=tool_version, standard_evaluate_items=standard_evaluate_items, callback_args=callback_args, client_token=client_token) + return sync_result_response(result) + except Exception as exc: + return error_response(str(exc)) + + if hasattr(mcp, "register_domain_tools"): + mcp.register_domain_tools("image", TOOL_NAMES) diff --git a/server/mcp_server_mediakit/src/mediakit/mcp_tools/video.py b/server/mcp_server_mediakit/src/mediakit/mcp_tools/video.py index b56213f9..13ce9c83 100644 --- a/server/mcp_server_mediakit/src/mediakit/mcp_tools/video.py +++ b/server/mcp_server_mediakit/src/mediakit/mcp_tools/video.py @@ -30,18 +30,80 @@ class EraseVideoSubtitleProEraseRatioLocationItem(TypedDict): bottom_right_x: Required[float] bottom_right_y: Required[float] -TOOL_NAMES = ['enhance_video', 'erase_video_subtitle_pro'] +TOOL_NAMES = ['analyze_video_highlights', 'analyze_video_storyline', 'asr_subtitles', 'enhance_video', 'enhance_video_generative', 'erase_video_subtitle', 'erase_video_subtitle_pro', 'generate_highlights_microdrama', 'generate_highlights_minigame', 'matte_greenscreen_video', 'matte_portrait_video', 'probe_video_metadata', 'segment_scenes', 'video_ocr'] def register_tools(mcp, client: MediKitClient) -> None: + @mcp.tool(name="analyze_video_highlights", description="智能捕捉视频\"情绪波峰\"与\"关键动作\",输出精准时间戳、高光打分、OCR 文本和画面描述等元数据,供下游进行更灵活的二次开发。\n支持短剧(Miniseries)和小游戏(Game)两种分析模型。\n使用限制:单次最多 100 个视频,累计时长不超过 300 分钟。 使用 task_id, 调用 query_task 方法获取结果") + async def analyze_video_highlights( + video_urls: List[str] = Field(..., description="待处理的视频 URL 列表,支持 1-100 个视频\n子项说明:视频 URL,支持 http:// 或 https:// 格式"), + model: str = Field(..., description="分析场景模型,Miniseries(短剧)或 Game(小游戏)"), + mode: str = Field(..., description="高光提取模式。固定组合为:model=Miniseries 时 mode 只能传 StorylineCuts;model=Game 时 mode 只能传 HighlightExtract"), + minigame_info: Optional[Dict[str, Any]] = Field(None, description="小游戏描述信息,当 model=Game 时可选填,可辅助模型更精准识别高光内容"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """智能捕捉视频"情绪波峰"与"关键动作",输出精准时间戳、高光打分、OCR 文本和画面描述等元数据,供下游进行更灵活的二次开发。 +支持短剧(Miniseries)和小游戏(Game)两种分析模型。 +使用限制:单次最多 100 个视频,累计时长不超过 300 分钟。""" + try: + result = client.call(api_name="analyze_video_highlights", video_urls=video_urls, model=model, mode=mode, minigame_info=minigame_info, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="analyze_video_storyline", description="智能解析影视剧内容,生成结构化剧情线,供智能剪辑、内容检索与互动播放等场景使用。\n基于大模型视频理解能力,对输入的单个或多个长视频(如电影、电视剧)进行分析,提取并组织成一份完整的故事线。\n该故事线由一系列按时间顺序排列的剧情片段(Clips)和基于片段聚合的高光故事线(Highlights)组成。\n使用限制:单次最多 30 个视频,单个视频时长不超过 2.5 小时。 使用 task_id, 调用 query_task 方法获取结果") + async def analyze_video_storyline( + video_urls: List[str] = Field(..., description="待处理的视频 URL 列表,支持 HTTP/HTTPS 公网可访问链接,最多 30 个视频\n子项说明:视频 URL,支持 http:// 或 https:// 格式"), + enable_snapshot: Optional[bool] = Field(False, description="是否为每个剧情片段生成关键帧快照。默认为 false。开启后,结果中将包含 clip_snapshot_url 字段"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """智能解析影视剧内容,生成结构化剧情线,供智能剪辑、内容检索与互动播放等场景使用。 +基于大模型视频理解能力,对输入的单个或多个长视频(如电影、电视剧)进行分析,提取并组织成一份完整的故事线。 +该故事线由一系列按时间顺序排列的剧情片段(Clips)和基于片段聚合的高光故事线(Highlights)组成。 +使用限制:单次最多 30 个视频,单个视频时长不超过 2.5 小时。""" + try: + result = client.call(api_name="analyze_video_storyline", video_urls=video_urls, enable_snapshot=enable_snapshot, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="asr_subtitles", description="对输入视频或音频进行语音识别,输出带时间戳的字幕片段。\n支持格式:主流音视频格式(如mp4、mov、mp3、m4a、wav等)。\n输入:video_url和audio_url二选一。 使用 task_id, 调用 query_task 方法获取结果") + async def asr_subtitles( + video_url: Optional[str] = Field(None, description="输入视频 Url(需公网可访问),与audio_url二选一,都存在时优先取video_url"), + audio_url: Optional[str] = Field(None, description="输入音频 Url(需公网可访问),与video_url二选一,不能都为空"), + content_type: Optional[str] = Field(None, description="识别类型,默认值为空,算法会自动探测类型,speech: 对话,singing: 歌唱"), + language: Optional[str] = Field(None, description="识别提示语言 ID (默认值为空,算法会自动探测语种)\n分类:简体中文,ID:cmn-Hans-CN\n分类:英语,ID:eng-US\n"), + enable_speaker_info: Optional[bool] = Field(False, description="是否开启说话人识别"), + enable_confidence: Optional[bool] = Field(False, description="是否返回置信度"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """对输入视频或音频进行语音识别,输出带时间戳的字幕片段。 +支持格式:主流音视频格式(如mp4、mov、mp3、m4a、wav等)。 +输入:video_url和audio_url二选一。""" + try: + result = client.call(api_name="asr_subtitles", video_url=video_url, audio_url=audio_url, content_type=content_type, language=language, enable_speaker_info=enable_speaker_info, enable_confidence=enable_confidence, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + @mcp.tool(name="enhance_video", description="画质增强:针对 AIGC / UGC / 短剧 / 教育 / 游戏 / 老片修复等场景,提供画质提升 + 超分增强一站式解决方案。依托 AI MediaKit 智能媒体处理引擎,融合视频内容理解、画质指标智能决策、多维度增强原子算法,实现画质的全面优化。\n支持格式:主流视频格式如mp4、flv、ts、avi、mov、wmv、mkv。\n使用限制:单文件大小不超过100G。 使用 task_id, 调用 query_task 方法获取结果") async def enhance_video( video_url: str = Field(..., description="输入视频。String 类型,支持http://xxx或https://xxx格式 URL"), scene: Optional[str] = Field('common', description="场景化模板类型。用于选择一个针对特定业务场景的预设画质增强模板。支持的取值如下:common(默认值): 通用模板;ugc: UGC 短视频;short_series: 短剧;aigc: AIGC 内容;old_film: 老片修复"), tool_version: Optional[str] = Field('standard', description="工具版本,标准版:standard,专业版:professional,默认为标准版"), resolution: Optional[str] = Field(None, description="目标分辨率。支持的取值如下所示。配置此参数后,不可同时配置resolution_limit字段"), - resolution_limit: Optional[int] = Field(None, description="目标长宽限制,用于指定输出视频的长边或短边的最大像素值,取值范围为 [64, 2160]。配置此参数后,不可同时配置resolution字段"), - fps: Optional[float] = Field(None, description="目标帧率,单位为 fps。取值范围为 (0, 120]。"), + resolution_limit: Optional[int] = Field(None, description="指定输出视频的短边像素值,取值范围为 [128, 2160]。设置后,系统将锁定视频的短边像素值为设定值,并在保持原视频宽高比的前提下,等比缩放至该限制值。示例:若原视频为 640x480 (4:3),设置 resolution_limit 为 720,则输出视频的短边将提升至 720,宽度等比缩放至 960。配置此参数后,不可同时配置resolution字段"), + bitrate_level: Optional[str] = Field('medium', description="码率档位。输出视频的目标平均码率。该参数将决定视频的视觉质量和最终的文件体积。参数取值:高码率、中码率(推荐码率)、低码率。非必填,默认为中码率。"), + fps: Optional[float] = Field(None, description="目标帧率,单位为 fps。取值范围为 [15, 120]。"), callback_args: Optional[str] = Field(None, description="可选,回调参数"), client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), *, @@ -51,7 +113,41 @@ async def enhance_video( 支持格式:主流视频格式如mp4、flv、ts、avi、mov、wmv、mkv。 使用限制:单文件大小不超过100G。""" try: - result = client.call(api_name="enhance_video", video_url=video_url, scene=scene, tool_version=tool_version, resolution=resolution, resolution_limit=resolution_limit, fps=fps, callback_args=callback_args, client_token=client_token) + result = client.call(api_name="enhance_video", video_url=video_url, scene=scene, tool_version=tool_version, resolution=resolution, resolution_limit=resolution_limit, bitrate_level=bitrate_level, fps=fps, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="enhance_video_generative", description="生成式视频增强修复(generative_video_restoration)是基于扩散大模型(Diffusion-based Large Model)的生成式视频修复技术。不仅可以还原被破坏的像素,更借助大规模预训练积累的丰富视觉先验,主动补全细节、理解语义,生成真实、自然、高保真的视频内容。 使用 task_id, 调用 query_task 方法获取结果") + async def enhance_video_generative( + video_url: str = Field(..., description="输入视频。String 类型,支持http://xxx或https://xxx格式 URL"), + resolution: Optional[str] = Field('720p', description="目标分辨率。支持的取值如下所示。"), + bitrate_level: Optional[str] = Field('medium', description="码率档位。输出视频的目标平均码率。该参数将决定视频的视觉质量和最终的文件体积。参数取值:高码率、中码率(推荐码率)、低码率。非必填,默认为中码率。"), + fps: Optional[float] = Field(None, description="目标帧率,单位为 fps。若未指定 fps 参数,输出视频将保持与原始片源一致的帧率。取值范围为 [15, 120]。建议不超过原片的 4 倍。"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """生成式视频增强修复(generative_video_restoration)是基于扩散大模型(Diffusion-based Large Model)的生成式视频修复技术。不仅可以还原被破坏的像素,更借助大规模预训练积累的丰富视觉先验,主动补全细节、理解语义,生成真实、自然、高保真的视频内容。""" + try: + result = client.call(api_name="enhance_video_generative", video_url=video_url, resolution=resolution, bitrate_level=bitrate_level, fps=fps, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="erase_video_subtitle", description="智能检测并擦除视频画面中已有的硬字幕,保留原始背景。\n支持格式:主流视频格式如mp4、flv、ts、avi、mov、wmv、mkv。 使用 task_id, 调用 query_task 方法获取结果") + async def erase_video_subtitle( + video_url: str = Field(..., description="输入视频。String 类型,支持http://xxx或https://xxx格式 Url"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """智能检测并擦除视频画面中已有的硬字幕,保留原始背景。 +支持格式:主流视频格式如mp4、flv、ts、avi、mov、wmv、mkv。""" + try: + result = client.call(api_name="erase_video_subtitle", video_url=video_url, callback_args=callback_args, client_token=client_token) return async_task_response(result) except Exception as exc: return error_response(str(exc)) @@ -75,5 +171,140 @@ async def erase_video_subtitle_pro( except Exception as exc: return error_response(str(exc)) + @mcp.tool(name="generate_highlights_microdrama", description="深度理解短剧角色、剧情与故事线,自动提取高光片段并混剪成投流视频。\n支持故事线混剪模式(StorylineCuts),可选\"短剧三要素\"视觉模板,输出高光集锦、单集预告等。\n支持输出详细分镜信息(storyboard)。\n使用限制:单次最多 100 个视频,累计时长不超过 300 分钟。 使用 task_id, 调用 query_task 方法获取结果") + async def generate_highlights_microdrama( + video_urls: List[str] = Field(..., description="待处理的短剧原片视频 URL 列表,支持 1-100 个视频\n子项说明:视频 URL,支持 http:// 或 https:// 格式"), + mode: Optional[str] = Field('StorylineCuts', description="短剧高光智剪模式,本期固定为 StorylineCuts(故事线混剪模式)"), + enable_generate_video: Optional[bool] = Field(True, description="是否生成混剪成片视频。true(默认)= 同时输出混剪视频与分镜信息;false = 仅输出高光分镜信息(clips/storyboard),不生成混剪视频,此时底层请求不会携带 Edit 字段,且传入的 edit_param 将被忽略。"), + enable_return_poster: Optional[bool] = Field(False, description="是否在结果中返回混剪视频封面图 URL。false(默认)= 不返回封面图;true = 若底层存在封面则返回 poster_url。"), + edit_param: Optional[Dict[str, Any]] = Field(None, description="成片剪辑参数配置"), + highlight_cuts_param: Optional[Dict[str, Any]] = Field(None, description="高光混剪参数配置"), + opening_hook_param: Optional[Dict[str, Any]] = Field(None, description="精彩前置功能参数配置(可选)"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """深度理解短剧角色、剧情与故事线,自动提取高光片段并混剪成投流视频。 +支持故事线混剪模式(StorylineCuts),可选"短剧三要素"视觉模板,输出高光集锦、单集预告等。 +支持输出详细分镜信息(storyboard)。 +使用限制:单次最多 100 个视频,累计时长不超过 300 分钟。""" + try: + result = client.call(api_name="generate_highlights_microdrama", video_urls=video_urls, mode=mode, enable_generate_video=enable_generate_video, enable_return_poster=enable_return_poster, edit_param=edit_param, highlight_cuts_param=highlight_cuts_param, opening_hook_param=opening_hook_param, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="generate_highlights_minigame", description="识别小游戏录屏视频中的核心玩法与高光事件(如连击、通关、极限操作等),\n快速生成用于买量的视频素材。支持提供游戏名称、玩法描述、高光定义以辅助模型更精准识别。\n使用限制:本期仅支持单视频输入。 使用 task_id, 调用 query_task 方法获取结果") + async def generate_highlights_minigame( + video_urls: List[str] = Field(..., description="待处理的小游戏视频 URL 列表,本期仅支持单视频输入\n子项说明:视频 URL,支持 http:// 或 https:// 格式"), + mode: Optional[str] = Field('HighlightExtract', description="高光提取模式,本期支持 HighlightExtract"), + enable_generate_video: Optional[bool] = Field(True, description="是否生成混剪成片视频。true(默认)= 同时输出混剪视频(Edit.Mode=HighlightClips)与高光片段信息;false = 仅输出高光片段信息(clips),底层请求不携带 Edit 字段,也不会生成任何混剪视频。"), + minigame_info: Optional[Dict[str, Any]] = Field(None, description="小游戏描述信息,建议填写以辅助模型更精准识别高光内容"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """识别小游戏录屏视频中的核心玩法与高光事件(如连击、通关、极限操作等), +快速生成用于买量的视频素材。支持提供游戏名称、玩法描述、高光定义以辅助模型更精准识别。 +使用限制:本期仅支持单视频输入。""" + try: + result = client.call(api_name="generate_highlights_minigame", video_urls=video_urls, mode=mode, enable_generate_video=enable_generate_video, minigame_info=minigame_info, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="matte_greenscreen_video", description="对以绿幕或纯色为背景的视频进行抠图,自动识别主体(人物、物品、动物等),同时移除背景,生成背景透明的视频。\n输出视频格式为 WEBM(默认)或 MOV,分辨率与原片对齐。\n支持的格式:主流视频格式如 mp4、flv、ts、avi、mov、mkv、wmv。 使用 task_id, 调用 query_task 方法获取结果") + async def matte_greenscreen_video( + video_url: str = Field(..., description="输入视频 Url。支持 http://xxx 或 https://xxx 格式。"), + format: Optional[str] = Field('WEBM', description="输出视频格式:MOV / WEBM(默认)"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """对以绿幕或纯色为背景的视频进行抠图,自动识别主体(人物、物品、动物等),同时移除背景,生成背景透明的视频。 +输出视频格式为 WEBM(默认)或 MOV,分辨率与原片对齐。 +支持的格式:主流视频格式如 mp4、flv、ts、avi、mov、mkv、wmv。""" + try: + result = client.call(api_name="matte_greenscreen_video", video_url=video_url, format=format, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="matte_portrait_video", description="自动识别人物主体,同时移除背景,生成背景透明的视频,适用于背景替换等场景。\n输出格式为 WEBM(默认)或 MOV,分辨率与原片对齐。\n支持的格式:主流视频格式如 mp4、flv、ts、avi、mov、mkv、wmv。 使用 task_id, 调用 query_task 方法获取结果") + async def matte_portrait_video( + video_url: str = Field(..., description="输入视频 Url。支持 http://xxx 或 https://xxx 格式。"), + format: Optional[str] = Field('WEBM', description="输出视频格式:MOV / WEBM(默认)"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """自动识别人物主体,同时移除背景,生成背景透明的视频,适用于背景替换等场景。 +输出格式为 WEBM(默认)或 MOV,分辨率与原片对齐。 +支持的格式:主流视频格式如 mp4、flv、ts、avi、mov、mkv、wmv。""" + try: + result = client.call(api_name="matte_portrait_video", video_url=video_url, format=format, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="probe_video_metadata", description="对输入视频 URL 进行探测,输出标准化媒资元信息,覆盖容器层(format_meta)、视频流层(video_stream_meta)与音频流层(audio_stream_meta)。\n字段分类参考 ffprobe,并对 VOD 原始返回做精简与统一,便于上层做分辨率/帧率/码率/编码等策略判断。\n使用限制:仅支持公网 HTTP/HTTPS URL;输入视频分辨率最高支持 4K。 使用 task_id, 调用 query_task 方法获取结果") + async def probe_video_metadata( + video_url: str = Field(..., description="待探测的视频公网 HTTP/HTTPS URL。"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """对输入视频 URL 进行探测,输出标准化媒资元信息,覆盖容器层(format_meta)、视频流层(video_stream_meta)与音频流层(audio_stream_meta)。 +字段分类参考 ffprobe,并对 VOD 原始返回做精简与统一,便于上层做分辨率/帧率/码率/编码等策略判断。 +使用限制:仅支持公网 HTTP/HTTPS URL;输入视频分辨率最高支持 4K。""" + try: + result = client.call(api_name="probe_video_metadata", video_url=video_url, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="segment_scenes", description="依据视频转场与画面变化自动切分场景,输出切片时间轴和(可选)切片文件。\n支持格式:MP4、FLV、ASF、RM、RMVB、MPEG、MOV、AVI、MPEGTS、M4S、WMV、3GP、TS、MPG、WEBM、MKV、WM、MPE、VOB、DAT、MP4V、M4V、F4V、MXF、QT 等主流视频格式。\n使用限制:单个视频时长不超过 2 小时。 使用 task_id, 调用 query_task 方法获取结果") + async def segment_scenes( + video_url: str = Field(..., description="待处理视频 Url,必须是公网可直接访问的 HTTP/HTTPS 链接"), + enable_clip_fade: Optional[bool] = Field(False, description="是否将检测到的淡入/淡出片段作为独立切片输出"), + segment_threshold: Optional[float] = Field(None, description="场景切分敏感度阈值,范围 [0, 100),100 不可取。数值越低切得越细,参考经验值10"), + min_duration: Optional[float] = Field(None, description="单个切片最小时长(秒),参考经验值3,应小于等于max_duration"), + max_duration: Optional[float] = Field(None, description="单个切片最大时长(秒),参考经验值30,应大于等于min_duration"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """依据视频转场与画面变化自动切分场景,输出切片时间轴和(可选)切片文件。 +支持格式:MP4、FLV、ASF、RM、RMVB、MPEG、MOV、AVI、MPEGTS、M4S、WMV、3GP、TS、MPG、WEBM、MKV、WM、MPE、VOB、DAT、MP4V、M4V、F4V、MXF、QT 等主流视频格式。 +使用限制:单个视频时长不超过 2 小时。""" + try: + result = client.call(api_name="segment_scenes", video_url=video_url, enable_clip_fade=enable_clip_fade, segment_threshold=segment_threshold, min_duration=min_duration, max_duration=max_duration, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + + @mcp.tool(name="video_ocr", description="识别视频画面中的字幕/文字内容,输出带时间戳的字幕片段。\n支持格式:主流视频格式如 mp4、flv、ts、avi、mov、wmv、mkv。 使用 task_id, 调用 query_task 方法获取结果") + async def video_ocr( + video_url: str = Field(..., description="输入视频 Url(需公网可访问)"), + mode: Optional[str] = Field('Subtitle', description="工作模式(Subtitle: 识别字幕文本;Detailed: 识别更详细文本信息)"), + callback_args: Optional[str] = Field(None, description="可选,回调参数"), + client_token: Optional[str] = Field(None, description="可选,用于幂等,默认幂等,用户可根据需求进行调整"), + *, + ctx: Context, + ) -> dict: + """识别视频画面中的字幕/文字内容,输出带时间戳的字幕片段。 +支持格式:主流视频格式如 mp4、flv、ts、avi、mov、wmv、mkv。""" + try: + result = client.call(api_name="video_ocr", video_url=video_url, mode=mode, callback_args=callback_args, client_token=client_token) + return async_task_response(result) + except Exception as exc: + return error_response(str(exc)) + if hasattr(mcp, "register_domain_tools"): mcp.register_domain_tools("video", TOOL_NAMES) diff --git a/server/mcp_server_mediakit/src/mediakit/utils/response.py b/server/mcp_server_mediakit/src/mediakit/utils/response.py index 19f34875..4aa95bae 100644 --- a/server/mcp_server_mediakit/src/mediakit/utils/response.py +++ b/server/mcp_server_mediakit/src/mediakit/utils/response.py @@ -63,6 +63,43 @@ def query_task_response(result: dict[str, Any]) -> dict[str, Any]: return output +def sync_result_response(result: dict[str, Any]) -> dict[str, Any]: + """同步处理任务 — API 网关返回 {success, task_id, request_id, result: {...}}。 + + 与 query_task_response 处理方式一致:保留 task_id/request_id/status,将 result 中的字段解构平铺到外层。 + + 业务级失败(HTTP 2xx 但 success=false)会被识别并转为 error 字段输出。 + + Returns: + 正向: { task_id, request_id, status?, ...业务字段 } + 失败: { task_id, request_id, error: "" } + """ + if not isinstance(result, dict): + return {} + + if result.get("success") is False: + return error_response( + result.get("error"), + task_id=result.get("task_id"), + request_id=result.get("request_id"), + ) + + output: dict[str, Any] = {} + task_id = result.get("task_id") + request_id = result.get("request_id") + status = result.get("status") + if task_id is not None: + output["task_id"] = task_id + if request_id is not None: + output["request_id"] = request_id + if status is not None: + output["status"] = status + inner = result.get("result") + if isinstance(inner, dict): + output.update(inner) + return output + + def error_response( error: dict[str, Any] | str | None = None, *, diff --git a/server/mcp_server_mediakit/tools.json b/server/mcp_server_mediakit/tools.json new file mode 100644 index 00000000..dcca023e --- /dev/null +++ b/server/mcp_server_mediakit/tools.json @@ -0,0 +1,626 @@ +[ + { + "name": "query_task", + "description": "查询异步任务状态。提交异步任务后使用此工具获取结果。推荐使用 poll_interval_seconds + max_poll_attempts 控制轮询,例如 poll_interval_seconds=2、max_poll_attempts=10。传递这两个参数时,不需要再传 poll_complete【不推荐】。单次调用时长 poll_interval_seconds * max_poll_attempts 建议不超过 30s,避免服务与 client 断链;若未查到终态,建议再次发起查询。", + "inputSchema": { + "properties": { + "task_id": { + "title": "Task Id", + "type": "string", + "description": "异步任务 ID,由异步能力提交后返回" + }, + "poll_interval_seconds": { + "default": 10, + "title": "Poll Interval Seconds", + "type": "number", + "description": "轮询间隔(秒)。推荐与 max_poll_attempts 搭配使用,例如 2。" + }, + "max_poll_attempts": { + "default": 0, + "title": "Max Poll Attempts", + "type": "integer", + "description": "最大轮询次数。0 表示仅查一次不轮询。推荐与 poll_interval_seconds 搭配使用,例如 10。" + }, + "poll_complete": { + "default": false, + "title": "Poll Complete", + "type": "boolean", + "description": "是否阻塞直到任务完成。推荐使用 poll_interval_seconds + max_poll_attempts 控制轮询;传递这两个参数时通常不需要再传 poll_complete。" + } + }, + "required": ["task_id"], + "title": "query_taskArguments", + "type": "object" + } + }, + { + "name": "add_image_to_video", + "description": "视频加图片,可用作加图片水印。 使用 task_id, 调用 query_task 方法获取结果", + "inputSchema": { + "properties": { + "video_url": { + "title": "Video Url", + "type": "string", + "description": "输入视频。String 类型,支持http://xxx或https://xxx格式 URL" + }, + "sub_image_url": { + "title": "Sub Image Url", + "type": "string", + "description": "图片URL。支持http://xxx或https://xxx格式 URL" + }, + "sub_image_height": { + "default": "5%", + "title": "Sub Image Height", + "type": "string", + "description": "图片的高度,字符串类型,支持具体像素值(如 '100')或百分比(如 '20%',相对于视频高度)。" + }, + "sub_image_width": { + "default": "10%", + "title": "Sub Image Width", + "type": "string", + "description": "图片的宽度,字符串类型,支持具体像素值(如 '100')或百分比(如 '20%',相对于视频高度)。" + }, + "sub_image_pos_x": { + "default": "85%", + "title": "Sub Image Pos X", + "type": "string", + "description": "图片在水平方向(X 轴)的位置,以视频左上角为原点,字符串类型,支持具体像素值或百分比。" + }, + "sub_image_pos_y": { + "default": "90%", + "title": "Sub Image Pos Y", + "type": "string", + "description": "图片在垂直方向(Y 轴)的位置,以视频左上角为原点,字符串类型,支持具体像素值或百分比。" + }, + "start_time": { + "default": null, + "title": "Start Time", + "type": "number", + "description": "图片的开始时间,单位:秒。不传默认同视频开始时间" + }, + "end_time": { + "default": null, + "title": "End Time", + "type": "number", + "description": "图片的结束时间,单位:秒。如设置超出原始视频时长,输出视频将以该结束时间为准,超出部分以黑屏形式延续。不传默认同视频结束时间" + }, + "callback_args": { + "default": null, + "title": "Callback Args", + "type": "string", + "description": "可选,回调参数" + }, + "client_token": { + "default": null, + "title": "Client Token", + "type": "string", + "description": "可选,用于幂等,默认幂等,用户可根据需求进行调整" + } + }, + "required": ["video_url", "sub_image_url"], + "title": "add_image_to_videoArguments", + "type": "object" + } + }, + { + "name": "add_subtitle_to_video", + "description": "将字幕文件或文本内容,以指定样式压制到视频画面中,生成带内嵌字幕的新视频。 使用 task_id, 调用 query_task 方法获取结果", + "inputSchema": { + "properties": { + "video_url": { + "title": "Video Url", + "type": "string", + "description": "输入视频。String 类型,支持http://xxx或https://xxx格式 URL" + }, + "subtitle_url": { + "default": null, + "title": "Subtitle Url", + "type": "string", + "description": "字幕文件 URL、filename。常见的字幕文件为 SRT、VTT、ASS 等格式。" + }, + "subtitles": { + "default": null, + "items": { + "type": "object", + "properties": { + "subtitle_text": { "type": "string" }, + "start_time": { "type": "number" }, + "end_time": { "type": "number" } + }, + "required": ["subtitle_text", "start_time", "end_time"] + }, + "title": "Subtitles", + "type": "array", + "description": "字幕列表,Array类型。" + }, + "subtitle_pos_preset": { + "default": "bottom_center", + "title": "Subtitle Pos Preset", + "type": "string", + "description": "预设字幕位置。bottom_center / top_center / center / lower_third" + }, + "subtitle_font_size": { + "default": 50, + "title": "Subtitle Font Size", + "type": "integer", + "description": "字幕的字体大小,单位:像素。" + }, + "subtitle_font_color": { + "default": "#FFFFFFFF", + "title": "Subtitle Font Color", + "type": "string", + "description": "字幕的字体颜色,RGBA 格式。默认 #FFFFFFFF" + }, + "subtitle_font_type": { + "default": "sy_black", + "title": "Subtitle Font Type", + "type": "string", + "description": "字幕的字体 ID。sy_black / pm_zhengdao / ali_puhui / zhanku_kuaile" + }, + "callback_args": { + "default": null, + "title": "Callback Args", + "type": "string", + "description": "可选,回调参数" + }, + "client_token": { + "default": null, + "title": "Client Token", + "type": "string", + "description": "可选,用于幂等,默认幂等" + } + }, + "required": ["video_url"], + "title": "add_subtitle_to_videoArguments", + "type": "object" + } + }, + { + "name": "adjust_video_speed", + "description": "调整视频的播放倍速,实现快放或慢放效果。 使用 task_id, 调用 query_task 方法获取结果", + "inputSchema": { + "properties": { + "video_url": { + "title": "Video Url", + "type": "string", + "description": "输入视频。String 类型,支持http://xxx或https://xxx格式 URL" + }, + "speed": { + "default": 1, + "title": "Speed", + "type": "number", + "description": "调整速度的倍数,Float类型,取值范围为 0.1~4。" + }, + "callback_args": { + "default": null, + "title": "Callback Args", + "type": "string" + }, + "client_token": { + "default": null, + "title": "Client Token", + "type": "string" + } + }, + "required": ["video_url"], + "title": "adjust_video_speedArguments", + "type": "object" + } + }, + { + "name": "concat_audio", + "description": "拼接多个音频片段。 使用 task_id, 调用 query_task 方法获取结果", + "inputSchema": { + "properties": { + "audio_urls": { + "items": { "type": "string" }, + "title": "Audio Urls", + "type": "array", + "description": "待拼接的音频列表,Array类型。最少传入1个,最多传入100个。子项为 http://xxx 或 https://xxx 格式 URL" + }, + "callback_args": { + "default": null, + "title": "Callback Args", + "type": "string" + }, + "client_token": { + "default": null, + "title": "Client Token", + "type": "string" + } + }, + "required": ["audio_urls"], + "title": "concat_audioArguments", + "type": "object" + } + }, + { + "name": "concat_video", + "description": "拼接多个视频片段,支持添加转场效果。 使用 task_id, 调用 query_task 方法获取结果", + "inputSchema": { + "properties": { + "video_urls": { + "items": { "type": "string" }, + "title": "Video Urls", + "type": "array", + "description": "待拼接的视频列表,Array类型。最少传入1个,最多传入100个。子项为 http://xxx 或 https://xxx 格式 URL" + }, + "transitions": { + "default": null, + "items": { "type": "string" }, + "title": "Transitions", + "type": "array", + "description": "转场效果 ID,Array 类型。可用 ID:1182359/1182360/1182358/1182365/1182367/1182368/1182369/1182370/1182373/1182374/1182375/1182378。如果不提供,则没有转场。当视频数量超过转场数量 2 个及以上时,系统将自动循环使用转场。" + }, + "callback_args": { + "default": null, + "title": "Callback Args", + "type": "string" + }, + "client_token": { + "default": null, + "title": "Client Token", + "type": "string" + } + }, + "required": ["video_urls"], + "title": "concat_videoArguments", + "type": "object" + } + }, + { + "name": "extract_audio", + "description": "将视频文件中的音频流分离并保存为独立的音频文件。 使用 task_id, 调用 query_task 方法获取结果", + "inputSchema": { + "properties": { + "video_url": { + "title": "Video Url", + "type": "string", + "description": "输入视频,String 类型,支持http://xxx或https://xxx格式 URL" + }, + "format": { + "default": "m4a", + "title": "Format", + "type": "string", + "description": "输出音频的格式,支持 mp3、m4a 格式。默认 m4a" + }, + "callback_args": { + "default": null, + "title": "Callback Args", + "type": "string" + }, + "client_token": { + "default": null, + "title": "Client Token", + "type": "string" + } + }, + "required": ["video_url"], + "title": "extract_audioArguments", + "type": "object" + } + }, + { + "name": "flip_video", + "description": "对视频画面进行上下或左右镜像翻转。 使用 task_id, 调用 query_task 方法获取结果", + "inputSchema": { + "properties": { + "video_url": { + "title": "Video Url", + "type": "string", + "description": "输入视频。String 类型,支持http://xxx或https://xxx格式 URL" + }, + "is_flip_vertical": { + "default": false, + "title": "Is Flip Vertical", + "type": "boolean", + "description": "是否进行垂直翻转。Boolean 类型,默认值 false。" + }, + "is_flip_horizontal": { + "default": false, + "title": "Is Flip Horizontal", + "type": "boolean", + "description": "是否进行水平翻转。Boolean 类型,默认值 false。" + }, + "callback_args": { + "default": null, + "title": "Callback Args", + "type": "string" + }, + "client_token": { + "default": null, + "title": "Client Token", + "type": "string" + } + }, + "required": ["video_url"], + "title": "flip_videoArguments", + "type": "object" + } + }, + { + "name": "image_to_video", + "description": "多张图片生成动画视频。 使用 task_id, 调用 query_task 方法获取结果", + "inputSchema": { + "properties": { + "images": { + "items": { + "type": "object", + "properties": { + "image_url": { "type": "string" }, + "duration": { "type": "number" }, + "animation_type": { "type": "string" }, + "animation_in": { "type": "number" }, + "animation_out": { "type": "number" } + }, + "required": ["image_url"] + }, + "title": "Images", + "type": "array", + "description": "待合成的图片列表,Array类型。最少传入1个,最多传入100个。" + }, + "transitions": { + "default": null, + "items": { "type": "string" }, + "title": "Transitions", + "type": "array", + "description": "转场效果 ID。可用 ID:1182359/1182360/1182358/1182365/1182367/1182368/1182369/1182370/1182373/1182374/1182375/1182378。" + }, + "callback_args": { + "default": null, + "title": "Callback Args", + "type": "string" + }, + "client_token": { + "default": null, + "title": "Client Token", + "type": "string" + } + }, + "required": ["images"], + "title": "image_to_videoArguments", + "type": "object" + } + }, + { + "name": "mux_audio_video", + "description": "音视频合成。 使用 task_id, 调用 query_task 方法获取结果", + "inputSchema": { + "properties": { + "video_url": { + "title": "Video Url", + "type": "string", + "description": "输入视频。String 类型,支持http://xxx或https://xxx格式 URL" + }, + "audio_url": { + "title": "Audio Url", + "type": "string", + "description": "输入音频。String 类型,支持http://xxx或https://xxx格式 URL" + }, + "is_audio_reserve": { + "default": true, + "title": "Is Audio Reserve", + "type": "boolean", + "description": "是否保留原视频流中的音频。默认值 true:保留。false:不保留。" + }, + "is_video_audio_sync": { + "default": false, + "title": "Is Video Audio Sync", + "type": "boolean", + "description": "是否对齐音频和视频时长。默认 false。" + }, + "sync_mode": { + "default": "video", + "title": "Sync Mode", + "type": "string", + "description": "is_video_audio_sync 为 true 时生效。可选 video / audio。" + }, + "sync_method": { + "default": "trim", + "title": "Sync Method", + "type": "string", + "description": "is_video_audio_sync 为 true 时生效。可选 speed / trim。" + }, + "callback_args": { + "default": null, + "title": "Callback Args", + "type": "string" + }, + "client_token": { + "default": null, + "title": "Client Token", + "type": "string" + } + }, + "required": ["video_url", "audio_url"], + "title": "mux_audio_videoArguments", + "type": "object" + } + }, + { + "name": "trim_audio", + "description": "按起止时间点(秒级)裁剪音频,生成新片段。 使用 task_id, 调用 query_task 方法获取结果", + "inputSchema": { + "properties": { + "audio_url": { + "title": "Audio Url", + "type": "string", + "description": "输入纯音频。String 类型,支持http://xxx或https://xxx格式 URL" + }, + "start_time": { + "default": 0, + "title": "Start Time", + "type": "number", + "description": "裁剪开始时间,默认为 0, 表示从头开始裁剪。支持设置为 2 位小数,单位:秒。" + }, + "end_time": { + "default": null, + "title": "End Time", + "type": "number", + "description": "裁剪结束时间,默认为片源结尾。支持设置为 2 位小数,单位:秒。" + }, + "callback_args": { + "default": null, + "title": "Callback Args", + "type": "string" + }, + "client_token": { + "default": null, + "title": "Client Token", + "type": "string" + } + }, + "required": ["audio_url"], + "title": "trim_audioArguments", + "type": "object" + } + }, + { + "name": "trim_video", + "description": "按起止时间点裁剪视频,生成新片段。 使用 task_id, 调用 query_task 方法获取结果", + "inputSchema": { + "properties": { + "video_url": { + "title": "Video Url", + "type": "string", + "description": "输入视频。String 类型,支持http://xxx或https://xxx格式 URL" + }, + "start_time": { + "default": 0, + "title": "Start Time", + "type": "number", + "description": "裁剪开始时间,默认为 0, 表示从头开始裁剪。支持设置为 2 位小数,单位:秒。" + }, + "end_time": { + "default": null, + "title": "End Time", + "type": "number", + "description": "裁剪结束时间,默认为片源结尾。支持设置为 2 位小数,单位:秒。" + }, + "callback_args": { + "default": null, + "title": "Callback Args", + "type": "string" + }, + "client_token": { + "default": null, + "title": "Client Token", + "type": "string" + } + }, + "required": ["video_url"], + "title": "trim_videoArguments", + "type": "object" + } + }, + { + "name": "enhance_video", + "description": "画质增强:针对 AIGC / UGC / 短剧 / 教育 / 游戏 / 老片修复等场景,提供画质提升 + 超分增强一站式解决方案。支持格式:mp4、flv、ts、avi、mov、wmv、mkv。单文件大小不超过 100G。 使用 task_id, 调用 query_task 方法获取结果", + "inputSchema": { + "properties": { + "video_url": { + "title": "Video Url", + "type": "string", + "description": "输入视频。String 类型,支持http://xxx或https://xxx格式 URL" + }, + "scene": { + "default": "common", + "title": "Scene", + "type": "string", + "description": "场景化模板类型。common / ugc / short_series / aigc / old_film" + }, + "tool_version": { + "default": "standard", + "title": "Tool Version", + "type": "string", + "description": "工具版本,标准版 standard,专业版 professional,默认 standard" + }, + "resolution": { + "default": null, + "title": "Resolution", + "type": "string", + "description": "目标分辨率。配置此参数后,不可同时配置 resolution_limit" + }, + "resolution_limit": { + "default": null, + "title": "Resolution Limit", + "type": "integer", + "description": "目标长宽限制,取值范围 [64, 2160]。配置此参数后,不可同时配置 resolution" + }, + "fps": { + "default": null, + "title": "Fps", + "type": "number", + "description": "目标帧率,单位为 fps。取值范围 (0, 120]。" + }, + "callback_args": { + "default": null, + "title": "Callback Args", + "type": "string" + }, + "client_token": { + "default": null, + "title": "Client Token", + "type": "string" + } + }, + "required": ["video_url"], + "title": "enhance_videoArguments", + "type": "object" + } + }, + { + "name": "erase_video_subtitle_pro", + "description": "针对视频中的字幕,实现高质量的无痕擦除,最大程度还原视频画面。支持格式:mp4、flv、ts、avi、mov、wmv、mkv。 使用 task_id, 调用 query_task 方法获取结果", + "inputSchema": { + "properties": { + "video_url": { + "title": "Video Url", + "type": "string", + "description": "输入视频。String 类型,支持http://xxx或https://xxx格式 URL" + }, + "mode": { + "default": "Subtitle", + "title": "Mode", + "type": "string", + "description": "字幕擦除模式:Subtitle / Text" + }, + "output_encode_mode": { + "default": "Quality", + "title": "Output Encode Mode", + "type": "string", + "description": "输出视频编码模式:Quality / Size" + }, + "erase_ratio_location": { + "default": null, + "items": { + "type": "object", + "properties": { + "top_left_x": { "type": "number" }, + "top_left_y": { "type": "number" }, + "bottom_right_x": { "type": "number" }, + "bottom_right_y": { "type": "number" } + }, + "required": ["top_left_x", "top_left_y", "bottom_right_x", "bottom_right_y"] + }, + "title": "Erase Ratio Location", + "type": "array", + "description": "擦除框数组。添加擦除框后,系统仅擦除框内文本。" + }, + "callback_args": { + "default": null, + "title": "Callback Args", + "type": "string" + }, + "client_token": { + "default": null, + "title": "Client Token", + "type": "string" + } + }, + "required": ["video_url"], + "title": "erase_video_subtitle_proArguments", + "type": "object" + } + } +] diff --git a/server/mcp_server_mediakit/trae.json b/server/mcp_server_mediakit/trae.json new file mode 100644 index 00000000..0a078e96 --- /dev/null +++ b/server/mcp_server_mediakit/trae.json @@ -0,0 +1,107 @@ +{ + "tools": [ + { + "name": "query_task", + "description": "Query asynchronous task status. After submitting an asynchronous task, use this tool to fetch its progress and result. Supports polling via poll_interval_seconds + max_poll_attempts." + }, + { + "name": "add_image_to_video", + "description": "Overlay an image on a video, commonly used for image watermarks. Supports configuring image size, position, start_time and end_time." + }, + { + "name": "add_subtitle_to_video", + "description": "Burn a subtitle file or subtitle text list into a video with configurable font, color, size and position presets." + }, + { + "name": "adjust_video_speed", + "description": "Adjust the playback speed of a video for fast/slow motion effects. Supports speed range 0.1~4." + }, + { + "name": "concat_audio", + "description": "Concatenate multiple audio clips into a single audio file." + }, + { + "name": "concat_video", + "description": "Concatenate multiple video clips into a new video, with optional transition effects." + }, + { + "name": "extract_audio", + "description": "Separate the audio stream from a video and save it as an independent audio file. Supports mp3 / m4a output." + }, + { + "name": "flip_video", + "description": "Flip a video vertically and/or horizontally." + }, + { + "name": "image_to_video", + "description": "Compose multiple images into an animated video, with configurable per-image duration, animation and optional transitions." + }, + { + "name": "mux_audio_video", + "description": "Mux audio and video into a single output, with optional duration alignment by trim or speed adjustment." + }, + { + "name": "trim_audio", + "description": "Trim an audio clip by specifying start_time and end_time in seconds." + }, + { + "name": "trim_video", + "description": "Trim a video clip by specifying start_time and end_time in seconds." + }, + { + "name": "enhance_video", + "description": "AI-powered video quality enhancement and super-resolution for AIGC / UGC / short series / education / gaming / old-film restoration scenarios." + }, + { + "name": "erase_video_subtitle_pro", + "description": "High-quality, traceless removal of subtitles from a video while preserving the original picture as much as possible." + } + ], + "servers": [ + { + "commands": { + "universal": { + "run": [ + { + "command": "uvx", + "args": [ + "--from", + "git+https://github.com/volcengine/mcp-server#subdirectory=server/mcp_server_mediakit", + "mcp-server-mediakit" + ], + "env": { + "MEDIAKIT_API_KEY": "Your MediaKit API Key", + "MEDIAKIT_ENDPOINT": "https://amk.cn-beijing.volces.com" + } + } + ] + } + }, + "configs": [ + { + "title": "MEDIAKIT_API_KEY", + "placeholder": "MEDIAKIT_API_KEY", + "required": true, + "key": "MEDIAKIT_API_KEY", + "url": "https://console.volcengine.com/imp/ai-mediakit/", + "type": "env", + "description": "Your MediaKit API Key", + "isPassword": true + }, + { + "title": "MEDIAKIT_ENDPOINT", + "placeholder": "MEDIAKIT_ENDPOINT", + "required": false, + "key": "MEDIAKIT_ENDPOINT", + "url": "https://console.volcengine.com/imp/ai-mediakit/", + "type": "env", + "description": "MediaKit service endpoint, default https://amk.cn-beijing.volces.com", + "isPassword": false + } + ], + "serverType": "stdio" + } + ], + "sourceType": "first_party", + "isFeatured": true +} From dcd947c6b9e19ceb5618767efce3278304ac8dfe Mon Sep 17 00:00:00 2001 From: tiehongji Date: Tue, 23 Jun 2026 11:36:35 +0800 Subject: [PATCH 6/6] =?UTF-8?q?feat:=20=E8=AE=A1=E8=B4=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/mcp_server_mediakit/README.md | 36 +- server/mcp_server_mediakit/README_zh.md | 34 +- server/mcp_server_mediakit/tools.json | 626 ------------------------ server/mcp_server_mediakit/trae.json | 107 ---- 4 files changed, 41 insertions(+), 762 deletions(-) delete mode 100644 server/mcp_server_mediakit/tools.json delete mode 100644 server/mcp_server_mediakit/trae.json diff --git a/server/mcp_server_mediakit/README.md b/server/mcp_server_mediakit/README.md index d41f4480..303a5b99 100644 --- a/server/mcp_server_mediakit/README.md +++ b/server/mcp_server_mediakit/README.md @@ -2,12 +2,12 @@ MediaKit MCP Server is a standard AI capability plugin for Volcano Engine AI MediaKit. It is built on the MCP (Model Context Protocol) protocol and exposes cloud media capabilities such as video editing, audio processing, subtitle processing, and video enhancement as tools that can be called by AI agents. With MediaKit MCP, developers can use natural language to drive intelligent media production workflows. -| Field | Value | -| --- | --- | -| Version | v1.0.0 | -| Description | MediaKit MCP intelligent media assistant | -| Categories | Media cloud, audio/video editing, video enhancement | -| Tags | MCP, MediaKit, video editing, audio processing, video enhancement | +| Field | Value | +| ----------- | ----------------------------------------------------------------- | +| Version | v1.0.0 | +| Description | MediaKit MCP intelligent media assistant | +| Categories | Media cloud, audio/video editing, video enhancement | +| Tags | MCP, MediaKit, video editing, audio processing, video enhancement | ## Tool Overview @@ -133,10 +133,10 @@ Trae is an AI-native IDE with strong agent collaboration capabilities. By connec Choose one of the following modes based on your usage scenario: -| Mode | Best for | Access method | -| --- | --- | --- | -| **Local Mode (JSON Local)** | Personal debugging, quick trials, no self-hosted service required. | Use `uvx` to launch MediaKit MCP directly from the `mcp-server` repository subdirectory. | -| **Cloud Mode (JSON URL)** | Team sharing, long-term usage, centralized operations. | Deploy MediaKit MCP Server yourself, then connect using the deployed Streamable HTTP endpoint. | +| Mode | Best for | Access method | +| --------------------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- | +| **Local Mode (JSON Local)** | Personal debugging, quick trials, no self-hosted service required. | Use `uvx` to launch MediaKit MCP directly from the `mcp-server` repository subdirectory. | +| **Cloud Mode (JSON URL)** | Team sharing, long-term usage, centralized operations. | Deploy MediaKit MCP Server yourself, then connect using the deployed Streamable HTTP endpoint. | ### Step 2: Add MCP Configuration @@ -317,11 +317,11 @@ The table below lists the core MediaKit MCP configuration fields for cloud mode For self-hosted cloud mode, you can also configure the following startup parameters: -| Environment variable | Default value | Description | -| --- | --- | --- | -| `MCP_SERVER_HOST` | `0.0.0.0` | MCP service bind address. | -| `MCP_SERVER_PORT` | `8000` | MCP service listening port. | -| `STREAMABLE_HTTP_PATH` | `/mcp` | Streamable HTTP path. | +| Environment variable | Default value | Description | +| ---------------------- | ------------- | --------------------------- | +| `MCP_SERVER_HOST` | `0.0.0.0` | MCP service bind address. | +| `MCP_SERVER_PORT` | `8000` | MCP service listening port. | +| `STREAMABLE_HTTP_PATH` | `/mcp` | Streamable HTTP path. | ## Tool Details @@ -384,3 +384,9 @@ Enhance video quality for scenarios such as `common`, `ugc`, `short_series`, `ai ## License MIT + +This software calls MediaKit APIs at runtime. Use of these APIs is subject to the following terms and privacy policies: + +- [Video Cloud Service Special Terms](https://www.volcengine.com/docs/6448/79646?lang=zh) +- [Intelligent Processing Service Billing Rules](https://www.volcengine.com/docs/6448/104992?lang=zh) +- [Intelligent Processing Service Level Agreement](https://www.volcengine.com/docs/6448/79648?lang=zh) diff --git a/server/mcp_server_mediakit/README_zh.md b/server/mcp_server_mediakit/README_zh.md index af723844..bcc5b845 100644 --- a/server/mcp_server_mediakit/README_zh.md +++ b/server/mcp_server_mediakit/README_zh.md @@ -2,11 +2,11 @@ MediaKit MCP 是火山引擎 AI MediaKit 面向 AI 时代推出的标准能力插件。它基于 MCP(Model Context Protocol)协议,将云端专业的视频剪辑、音频处理、字幕处理、画质增强等原子能力封装为智能体可直观调用的工具。通过 MediaKit MCP,开发者可直接以自然语言驱动 AI 智能体完成复杂的云端媒体处理任务。 -| 字段 | 取值 | -| --- | --- | -| 版本 | v1.0.0 | -| 描述 | MediaKit MCP 智能媒体助手 | -| 分类 | 视频云、音视频编辑、画质增强 | +| 字段 | 取值 | +| ---- | ------------------------------------------- | +| 版本 | v1.0.0 | +| 描述 | MediaKit MCP 智能媒体助手 | +| 分类 | 视频云、音视频编辑、画质增强 | | 标签 | MCP、MediaKit、视频剪辑、音频处理、画质增强 | ## 工具概览 @@ -133,10 +133,10 @@ Trae 是一款 AI 原生 IDE,提供了强大的智能体协作能力。通过 根据您的使用场景,选择以下两种接入模式之一: -| 模式 | 适用场景 | 接入方式 | -| --- | --- | --- | -| **本地模式(JSON Local)** | 个人调试、快速试用、无需自建服务。 | 通过 `uvx` 直接从 `mcp-server` 仓库子目录拉起 MediaKit MCP。 | -| **云端模式(JSON URL)** | 团队共享、长期稳定使用、统一运维。 | 先自行部署 MediaKit MCP Server,再使用部署后的 Streamable HTTP 地址接入。 | +| 模式 | 适用场景 | 接入方式 | +| -------------------------- | ---------------------------------- | ------------------------------------------------------------------------- | +| **本地模式(JSON Local)** | 个人调试、快速试用、无需自建服务。 | 通过 `uvx` 直接从 `mcp-server` 仓库子目录拉起 MediaKit MCP。 | +| **云端模式(JSON URL)** | 团队共享、长期稳定使用、统一运维。 | 先自行部署 MediaKit MCP Server,再使用部署后的 Streamable HTTP 地址接入。 | ### 步骤 2:添加 MCP 配置 @@ -317,11 +317,11 @@ uvx --from "git+https://github.com/volcengine/mcp-server.git#subdirectory=server 云端自部署时,还可按需使用以下服务启动参数: -| 环境变量 | 默认值 | 说明 | -| --- | --- | --- | -| `MCP_SERVER_HOST` | `0.0.0.0` | MCP 服务监听地址。 | -| `MCP_SERVER_PORT` | `8000` | MCP 服务监听端口。 | -| `STREAMABLE_HTTP_PATH` | `/mcp` | Streamable HTTP 路径。 | +| 环境变量 | 默认值 | 说明 | +| ---------------------- | --------- | ---------------------- | +| `MCP_SERVER_HOST` | `0.0.0.0` | MCP 服务监听地址。 | +| `MCP_SERVER_PORT` | `8000` | MCP 服务监听端口。 | +| `STREAMABLE_HTTP_PATH` | `/mcp` | Streamable HTTP 路径。 | ## 工具详情 @@ -384,3 +384,9 @@ uvx --from "git+https://github.com/volcengine/mcp-server.git#subdirectory=server ## License MIT + +该软件运行时会调用 MediaKit 的 API,使用这些 API 需要遵守如下协议和隐私政策: + +- [视频云服务专用条款](https://www.volcengine.com/docs/6448/79646?lang=zh) +- [智能处理服务计费结算规则](https://www.volcengine.com/docs/6448/104992?lang=zh) +- [智能处理服务等级协议](https://www.volcengine.com/docs/6448/79648?lang=zh) diff --git a/server/mcp_server_mediakit/tools.json b/server/mcp_server_mediakit/tools.json deleted file mode 100644 index dcca023e..00000000 --- a/server/mcp_server_mediakit/tools.json +++ /dev/null @@ -1,626 +0,0 @@ -[ - { - "name": "query_task", - "description": "查询异步任务状态。提交异步任务后使用此工具获取结果。推荐使用 poll_interval_seconds + max_poll_attempts 控制轮询,例如 poll_interval_seconds=2、max_poll_attempts=10。传递这两个参数时,不需要再传 poll_complete【不推荐】。单次调用时长 poll_interval_seconds * max_poll_attempts 建议不超过 30s,避免服务与 client 断链;若未查到终态,建议再次发起查询。", - "inputSchema": { - "properties": { - "task_id": { - "title": "Task Id", - "type": "string", - "description": "异步任务 ID,由异步能力提交后返回" - }, - "poll_interval_seconds": { - "default": 10, - "title": "Poll Interval Seconds", - "type": "number", - "description": "轮询间隔(秒)。推荐与 max_poll_attempts 搭配使用,例如 2。" - }, - "max_poll_attempts": { - "default": 0, - "title": "Max Poll Attempts", - "type": "integer", - "description": "最大轮询次数。0 表示仅查一次不轮询。推荐与 poll_interval_seconds 搭配使用,例如 10。" - }, - "poll_complete": { - "default": false, - "title": "Poll Complete", - "type": "boolean", - "description": "是否阻塞直到任务完成。推荐使用 poll_interval_seconds + max_poll_attempts 控制轮询;传递这两个参数时通常不需要再传 poll_complete。" - } - }, - "required": ["task_id"], - "title": "query_taskArguments", - "type": "object" - } - }, - { - "name": "add_image_to_video", - "description": "视频加图片,可用作加图片水印。 使用 task_id, 调用 query_task 方法获取结果", - "inputSchema": { - "properties": { - "video_url": { - "title": "Video Url", - "type": "string", - "description": "输入视频。String 类型,支持http://xxx或https://xxx格式 URL" - }, - "sub_image_url": { - "title": "Sub Image Url", - "type": "string", - "description": "图片URL。支持http://xxx或https://xxx格式 URL" - }, - "sub_image_height": { - "default": "5%", - "title": "Sub Image Height", - "type": "string", - "description": "图片的高度,字符串类型,支持具体像素值(如 '100')或百分比(如 '20%',相对于视频高度)。" - }, - "sub_image_width": { - "default": "10%", - "title": "Sub Image Width", - "type": "string", - "description": "图片的宽度,字符串类型,支持具体像素值(如 '100')或百分比(如 '20%',相对于视频高度)。" - }, - "sub_image_pos_x": { - "default": "85%", - "title": "Sub Image Pos X", - "type": "string", - "description": "图片在水平方向(X 轴)的位置,以视频左上角为原点,字符串类型,支持具体像素值或百分比。" - }, - "sub_image_pos_y": { - "default": "90%", - "title": "Sub Image Pos Y", - "type": "string", - "description": "图片在垂直方向(Y 轴)的位置,以视频左上角为原点,字符串类型,支持具体像素值或百分比。" - }, - "start_time": { - "default": null, - "title": "Start Time", - "type": "number", - "description": "图片的开始时间,单位:秒。不传默认同视频开始时间" - }, - "end_time": { - "default": null, - "title": "End Time", - "type": "number", - "description": "图片的结束时间,单位:秒。如设置超出原始视频时长,输出视频将以该结束时间为准,超出部分以黑屏形式延续。不传默认同视频结束时间" - }, - "callback_args": { - "default": null, - "title": "Callback Args", - "type": "string", - "description": "可选,回调参数" - }, - "client_token": { - "default": null, - "title": "Client Token", - "type": "string", - "description": "可选,用于幂等,默认幂等,用户可根据需求进行调整" - } - }, - "required": ["video_url", "sub_image_url"], - "title": "add_image_to_videoArguments", - "type": "object" - } - }, - { - "name": "add_subtitle_to_video", - "description": "将字幕文件或文本内容,以指定样式压制到视频画面中,生成带内嵌字幕的新视频。 使用 task_id, 调用 query_task 方法获取结果", - "inputSchema": { - "properties": { - "video_url": { - "title": "Video Url", - "type": "string", - "description": "输入视频。String 类型,支持http://xxx或https://xxx格式 URL" - }, - "subtitle_url": { - "default": null, - "title": "Subtitle Url", - "type": "string", - "description": "字幕文件 URL、filename。常见的字幕文件为 SRT、VTT、ASS 等格式。" - }, - "subtitles": { - "default": null, - "items": { - "type": "object", - "properties": { - "subtitle_text": { "type": "string" }, - "start_time": { "type": "number" }, - "end_time": { "type": "number" } - }, - "required": ["subtitle_text", "start_time", "end_time"] - }, - "title": "Subtitles", - "type": "array", - "description": "字幕列表,Array类型。" - }, - "subtitle_pos_preset": { - "default": "bottom_center", - "title": "Subtitle Pos Preset", - "type": "string", - "description": "预设字幕位置。bottom_center / top_center / center / lower_third" - }, - "subtitle_font_size": { - "default": 50, - "title": "Subtitle Font Size", - "type": "integer", - "description": "字幕的字体大小,单位:像素。" - }, - "subtitle_font_color": { - "default": "#FFFFFFFF", - "title": "Subtitle Font Color", - "type": "string", - "description": "字幕的字体颜色,RGBA 格式。默认 #FFFFFFFF" - }, - "subtitle_font_type": { - "default": "sy_black", - "title": "Subtitle Font Type", - "type": "string", - "description": "字幕的字体 ID。sy_black / pm_zhengdao / ali_puhui / zhanku_kuaile" - }, - "callback_args": { - "default": null, - "title": "Callback Args", - "type": "string", - "description": "可选,回调参数" - }, - "client_token": { - "default": null, - "title": "Client Token", - "type": "string", - "description": "可选,用于幂等,默认幂等" - } - }, - "required": ["video_url"], - "title": "add_subtitle_to_videoArguments", - "type": "object" - } - }, - { - "name": "adjust_video_speed", - "description": "调整视频的播放倍速,实现快放或慢放效果。 使用 task_id, 调用 query_task 方法获取结果", - "inputSchema": { - "properties": { - "video_url": { - "title": "Video Url", - "type": "string", - "description": "输入视频。String 类型,支持http://xxx或https://xxx格式 URL" - }, - "speed": { - "default": 1, - "title": "Speed", - "type": "number", - "description": "调整速度的倍数,Float类型,取值范围为 0.1~4。" - }, - "callback_args": { - "default": null, - "title": "Callback Args", - "type": "string" - }, - "client_token": { - "default": null, - "title": "Client Token", - "type": "string" - } - }, - "required": ["video_url"], - "title": "adjust_video_speedArguments", - "type": "object" - } - }, - { - "name": "concat_audio", - "description": "拼接多个音频片段。 使用 task_id, 调用 query_task 方法获取结果", - "inputSchema": { - "properties": { - "audio_urls": { - "items": { "type": "string" }, - "title": "Audio Urls", - "type": "array", - "description": "待拼接的音频列表,Array类型。最少传入1个,最多传入100个。子项为 http://xxx 或 https://xxx 格式 URL" - }, - "callback_args": { - "default": null, - "title": "Callback Args", - "type": "string" - }, - "client_token": { - "default": null, - "title": "Client Token", - "type": "string" - } - }, - "required": ["audio_urls"], - "title": "concat_audioArguments", - "type": "object" - } - }, - { - "name": "concat_video", - "description": "拼接多个视频片段,支持添加转场效果。 使用 task_id, 调用 query_task 方法获取结果", - "inputSchema": { - "properties": { - "video_urls": { - "items": { "type": "string" }, - "title": "Video Urls", - "type": "array", - "description": "待拼接的视频列表,Array类型。最少传入1个,最多传入100个。子项为 http://xxx 或 https://xxx 格式 URL" - }, - "transitions": { - "default": null, - "items": { "type": "string" }, - "title": "Transitions", - "type": "array", - "description": "转场效果 ID,Array 类型。可用 ID:1182359/1182360/1182358/1182365/1182367/1182368/1182369/1182370/1182373/1182374/1182375/1182378。如果不提供,则没有转场。当视频数量超过转场数量 2 个及以上时,系统将自动循环使用转场。" - }, - "callback_args": { - "default": null, - "title": "Callback Args", - "type": "string" - }, - "client_token": { - "default": null, - "title": "Client Token", - "type": "string" - } - }, - "required": ["video_urls"], - "title": "concat_videoArguments", - "type": "object" - } - }, - { - "name": "extract_audio", - "description": "将视频文件中的音频流分离并保存为独立的音频文件。 使用 task_id, 调用 query_task 方法获取结果", - "inputSchema": { - "properties": { - "video_url": { - "title": "Video Url", - "type": "string", - "description": "输入视频,String 类型,支持http://xxx或https://xxx格式 URL" - }, - "format": { - "default": "m4a", - "title": "Format", - "type": "string", - "description": "输出音频的格式,支持 mp3、m4a 格式。默认 m4a" - }, - "callback_args": { - "default": null, - "title": "Callback Args", - "type": "string" - }, - "client_token": { - "default": null, - "title": "Client Token", - "type": "string" - } - }, - "required": ["video_url"], - "title": "extract_audioArguments", - "type": "object" - } - }, - { - "name": "flip_video", - "description": "对视频画面进行上下或左右镜像翻转。 使用 task_id, 调用 query_task 方法获取结果", - "inputSchema": { - "properties": { - "video_url": { - "title": "Video Url", - "type": "string", - "description": "输入视频。String 类型,支持http://xxx或https://xxx格式 URL" - }, - "is_flip_vertical": { - "default": false, - "title": "Is Flip Vertical", - "type": "boolean", - "description": "是否进行垂直翻转。Boolean 类型,默认值 false。" - }, - "is_flip_horizontal": { - "default": false, - "title": "Is Flip Horizontal", - "type": "boolean", - "description": "是否进行水平翻转。Boolean 类型,默认值 false。" - }, - "callback_args": { - "default": null, - "title": "Callback Args", - "type": "string" - }, - "client_token": { - "default": null, - "title": "Client Token", - "type": "string" - } - }, - "required": ["video_url"], - "title": "flip_videoArguments", - "type": "object" - } - }, - { - "name": "image_to_video", - "description": "多张图片生成动画视频。 使用 task_id, 调用 query_task 方法获取结果", - "inputSchema": { - "properties": { - "images": { - "items": { - "type": "object", - "properties": { - "image_url": { "type": "string" }, - "duration": { "type": "number" }, - "animation_type": { "type": "string" }, - "animation_in": { "type": "number" }, - "animation_out": { "type": "number" } - }, - "required": ["image_url"] - }, - "title": "Images", - "type": "array", - "description": "待合成的图片列表,Array类型。最少传入1个,最多传入100个。" - }, - "transitions": { - "default": null, - "items": { "type": "string" }, - "title": "Transitions", - "type": "array", - "description": "转场效果 ID。可用 ID:1182359/1182360/1182358/1182365/1182367/1182368/1182369/1182370/1182373/1182374/1182375/1182378。" - }, - "callback_args": { - "default": null, - "title": "Callback Args", - "type": "string" - }, - "client_token": { - "default": null, - "title": "Client Token", - "type": "string" - } - }, - "required": ["images"], - "title": "image_to_videoArguments", - "type": "object" - } - }, - { - "name": "mux_audio_video", - "description": "音视频合成。 使用 task_id, 调用 query_task 方法获取结果", - "inputSchema": { - "properties": { - "video_url": { - "title": "Video Url", - "type": "string", - "description": "输入视频。String 类型,支持http://xxx或https://xxx格式 URL" - }, - "audio_url": { - "title": "Audio Url", - "type": "string", - "description": "输入音频。String 类型,支持http://xxx或https://xxx格式 URL" - }, - "is_audio_reserve": { - "default": true, - "title": "Is Audio Reserve", - "type": "boolean", - "description": "是否保留原视频流中的音频。默认值 true:保留。false:不保留。" - }, - "is_video_audio_sync": { - "default": false, - "title": "Is Video Audio Sync", - "type": "boolean", - "description": "是否对齐音频和视频时长。默认 false。" - }, - "sync_mode": { - "default": "video", - "title": "Sync Mode", - "type": "string", - "description": "is_video_audio_sync 为 true 时生效。可选 video / audio。" - }, - "sync_method": { - "default": "trim", - "title": "Sync Method", - "type": "string", - "description": "is_video_audio_sync 为 true 时生效。可选 speed / trim。" - }, - "callback_args": { - "default": null, - "title": "Callback Args", - "type": "string" - }, - "client_token": { - "default": null, - "title": "Client Token", - "type": "string" - } - }, - "required": ["video_url", "audio_url"], - "title": "mux_audio_videoArguments", - "type": "object" - } - }, - { - "name": "trim_audio", - "description": "按起止时间点(秒级)裁剪音频,生成新片段。 使用 task_id, 调用 query_task 方法获取结果", - "inputSchema": { - "properties": { - "audio_url": { - "title": "Audio Url", - "type": "string", - "description": "输入纯音频。String 类型,支持http://xxx或https://xxx格式 URL" - }, - "start_time": { - "default": 0, - "title": "Start Time", - "type": "number", - "description": "裁剪开始时间,默认为 0, 表示从头开始裁剪。支持设置为 2 位小数,单位:秒。" - }, - "end_time": { - "default": null, - "title": "End Time", - "type": "number", - "description": "裁剪结束时间,默认为片源结尾。支持设置为 2 位小数,单位:秒。" - }, - "callback_args": { - "default": null, - "title": "Callback Args", - "type": "string" - }, - "client_token": { - "default": null, - "title": "Client Token", - "type": "string" - } - }, - "required": ["audio_url"], - "title": "trim_audioArguments", - "type": "object" - } - }, - { - "name": "trim_video", - "description": "按起止时间点裁剪视频,生成新片段。 使用 task_id, 调用 query_task 方法获取结果", - "inputSchema": { - "properties": { - "video_url": { - "title": "Video Url", - "type": "string", - "description": "输入视频。String 类型,支持http://xxx或https://xxx格式 URL" - }, - "start_time": { - "default": 0, - "title": "Start Time", - "type": "number", - "description": "裁剪开始时间,默认为 0, 表示从头开始裁剪。支持设置为 2 位小数,单位:秒。" - }, - "end_time": { - "default": null, - "title": "End Time", - "type": "number", - "description": "裁剪结束时间,默认为片源结尾。支持设置为 2 位小数,单位:秒。" - }, - "callback_args": { - "default": null, - "title": "Callback Args", - "type": "string" - }, - "client_token": { - "default": null, - "title": "Client Token", - "type": "string" - } - }, - "required": ["video_url"], - "title": "trim_videoArguments", - "type": "object" - } - }, - { - "name": "enhance_video", - "description": "画质增强:针对 AIGC / UGC / 短剧 / 教育 / 游戏 / 老片修复等场景,提供画质提升 + 超分增强一站式解决方案。支持格式:mp4、flv、ts、avi、mov、wmv、mkv。单文件大小不超过 100G。 使用 task_id, 调用 query_task 方法获取结果", - "inputSchema": { - "properties": { - "video_url": { - "title": "Video Url", - "type": "string", - "description": "输入视频。String 类型,支持http://xxx或https://xxx格式 URL" - }, - "scene": { - "default": "common", - "title": "Scene", - "type": "string", - "description": "场景化模板类型。common / ugc / short_series / aigc / old_film" - }, - "tool_version": { - "default": "standard", - "title": "Tool Version", - "type": "string", - "description": "工具版本,标准版 standard,专业版 professional,默认 standard" - }, - "resolution": { - "default": null, - "title": "Resolution", - "type": "string", - "description": "目标分辨率。配置此参数后,不可同时配置 resolution_limit" - }, - "resolution_limit": { - "default": null, - "title": "Resolution Limit", - "type": "integer", - "description": "目标长宽限制,取值范围 [64, 2160]。配置此参数后,不可同时配置 resolution" - }, - "fps": { - "default": null, - "title": "Fps", - "type": "number", - "description": "目标帧率,单位为 fps。取值范围 (0, 120]。" - }, - "callback_args": { - "default": null, - "title": "Callback Args", - "type": "string" - }, - "client_token": { - "default": null, - "title": "Client Token", - "type": "string" - } - }, - "required": ["video_url"], - "title": "enhance_videoArguments", - "type": "object" - } - }, - { - "name": "erase_video_subtitle_pro", - "description": "针对视频中的字幕,实现高质量的无痕擦除,最大程度还原视频画面。支持格式:mp4、flv、ts、avi、mov、wmv、mkv。 使用 task_id, 调用 query_task 方法获取结果", - "inputSchema": { - "properties": { - "video_url": { - "title": "Video Url", - "type": "string", - "description": "输入视频。String 类型,支持http://xxx或https://xxx格式 URL" - }, - "mode": { - "default": "Subtitle", - "title": "Mode", - "type": "string", - "description": "字幕擦除模式:Subtitle / Text" - }, - "output_encode_mode": { - "default": "Quality", - "title": "Output Encode Mode", - "type": "string", - "description": "输出视频编码模式:Quality / Size" - }, - "erase_ratio_location": { - "default": null, - "items": { - "type": "object", - "properties": { - "top_left_x": { "type": "number" }, - "top_left_y": { "type": "number" }, - "bottom_right_x": { "type": "number" }, - "bottom_right_y": { "type": "number" } - }, - "required": ["top_left_x", "top_left_y", "bottom_right_x", "bottom_right_y"] - }, - "title": "Erase Ratio Location", - "type": "array", - "description": "擦除框数组。添加擦除框后,系统仅擦除框内文本。" - }, - "callback_args": { - "default": null, - "title": "Callback Args", - "type": "string" - }, - "client_token": { - "default": null, - "title": "Client Token", - "type": "string" - } - }, - "required": ["video_url"], - "title": "erase_video_subtitle_proArguments", - "type": "object" - } - } -] diff --git a/server/mcp_server_mediakit/trae.json b/server/mcp_server_mediakit/trae.json deleted file mode 100644 index 0a078e96..00000000 --- a/server/mcp_server_mediakit/trae.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "tools": [ - { - "name": "query_task", - "description": "Query asynchronous task status. After submitting an asynchronous task, use this tool to fetch its progress and result. Supports polling via poll_interval_seconds + max_poll_attempts." - }, - { - "name": "add_image_to_video", - "description": "Overlay an image on a video, commonly used for image watermarks. Supports configuring image size, position, start_time and end_time." - }, - { - "name": "add_subtitle_to_video", - "description": "Burn a subtitle file or subtitle text list into a video with configurable font, color, size and position presets." - }, - { - "name": "adjust_video_speed", - "description": "Adjust the playback speed of a video for fast/slow motion effects. Supports speed range 0.1~4." - }, - { - "name": "concat_audio", - "description": "Concatenate multiple audio clips into a single audio file." - }, - { - "name": "concat_video", - "description": "Concatenate multiple video clips into a new video, with optional transition effects." - }, - { - "name": "extract_audio", - "description": "Separate the audio stream from a video and save it as an independent audio file. Supports mp3 / m4a output." - }, - { - "name": "flip_video", - "description": "Flip a video vertically and/or horizontally." - }, - { - "name": "image_to_video", - "description": "Compose multiple images into an animated video, with configurable per-image duration, animation and optional transitions." - }, - { - "name": "mux_audio_video", - "description": "Mux audio and video into a single output, with optional duration alignment by trim or speed adjustment." - }, - { - "name": "trim_audio", - "description": "Trim an audio clip by specifying start_time and end_time in seconds." - }, - { - "name": "trim_video", - "description": "Trim a video clip by specifying start_time and end_time in seconds." - }, - { - "name": "enhance_video", - "description": "AI-powered video quality enhancement and super-resolution for AIGC / UGC / short series / education / gaming / old-film restoration scenarios." - }, - { - "name": "erase_video_subtitle_pro", - "description": "High-quality, traceless removal of subtitles from a video while preserving the original picture as much as possible." - } - ], - "servers": [ - { - "commands": { - "universal": { - "run": [ - { - "command": "uvx", - "args": [ - "--from", - "git+https://github.com/volcengine/mcp-server#subdirectory=server/mcp_server_mediakit", - "mcp-server-mediakit" - ], - "env": { - "MEDIAKIT_API_KEY": "Your MediaKit API Key", - "MEDIAKIT_ENDPOINT": "https://amk.cn-beijing.volces.com" - } - } - ] - } - }, - "configs": [ - { - "title": "MEDIAKIT_API_KEY", - "placeholder": "MEDIAKIT_API_KEY", - "required": true, - "key": "MEDIAKIT_API_KEY", - "url": "https://console.volcengine.com/imp/ai-mediakit/", - "type": "env", - "description": "Your MediaKit API Key", - "isPassword": true - }, - { - "title": "MEDIAKIT_ENDPOINT", - "placeholder": "MEDIAKIT_ENDPOINT", - "required": false, - "key": "MEDIAKIT_ENDPOINT", - "url": "https://console.volcengine.com/imp/ai-mediakit/", - "type": "env", - "description": "MediaKit service endpoint, default https://amk.cn-beijing.volces.com", - "isPassword": false - } - ], - "serverType": "stdio" - } - ], - "sourceType": "first_party", - "isFeatured": true -}