From 03ec01568f257a8e9900414769dbb406104aaec6 Mon Sep 17 00:00:00 2001 From: YangSen-qn Date: Sun, 27 Apr 2025 17:01:16 +0800 Subject: [PATCH 1/5] fix: get url auth --- .../core/media_processing/__init__.py | 2 +- src/mcp_server/core/media_processing/tools.py | 26 +++++++----- src/mcp_server/core/media_processing/utils.py | 40 ++++++++++++++++++- 3 files changed, 54 insertions(+), 14 deletions(-) diff --git a/src/mcp_server/core/media_processing/__init__.py b/src/mcp_server/core/media_processing/__init__.py index 79bcca7..94ff9db 100644 --- a/src/mcp_server/core/media_processing/__init__.py +++ b/src/mcp_server/core/media_processing/__init__.py @@ -5,7 +5,7 @@ def load(cfg: config.Config): cli = processing.MediaProcessingService(cfg) - register_tools(cli) + register_tools(cfg, cli) __all__ = [ diff --git a/src/mcp_server/core/media_processing/tools.py b/src/mcp_server/core/media_processing/tools.py index 350cac8..672c2f1 100644 --- a/src/mcp_server/core/media_processing/tools.py +++ b/src/mcp_server/core/media_processing/tools.py @@ -1,8 +1,11 @@ import logging + +import qiniu from mcp import types from . import utils from .processing import MediaProcessingService +from ...config import config from ...consts import consts from ...tools import tools @@ -12,7 +15,8 @@ class _ToolImpl: - def __init__(self, cli: MediaProcessingService): + def __init__(self, cfg: config.Config, cli: MediaProcessingService): + self.auth = qiniu.Auth(cfg.access_key, cfg.secret_key) self.client = cli @tools.tool_meta( @@ -55,8 +59,8 @@ def image_scale_by_percent( types.TextContent(type="text", text="percent must be between 1 and 999") ] - fop = f"imageMogr2/thumbnail/!{percent}p" - object_url = utils.url_add_processing_func(object_url, fop) + func = f"imageMogr2/thumbnail/!{percent}p" + object_url = utils.url_add_processing_func(auth=self.auth, url=object_url, func=func) return [ types.TextContent( type="text", @@ -111,16 +115,16 @@ def image_scale_by_size( if object_url is None or len(object_url) == 0: return [types.TextContent(type="text", text="object_url is required")] - fop = f"{width}x{height}" - if len(fop) == 1: + func = f"{width}x{height}" + if len(func) == 1: return [ types.TextContent( type="text", text="At least one width or height must be set" ) ] - fop = f"imageMogr2/thumbnail/{fop}" - object_url = utils.url_add_processing_func(object_url, fop) + func = f"imageMogr2/thumbnail/{func}" + object_url = utils.url_add_processing_func(auth=self.auth, url=object_url, func=func) return [ types.TextContent( type="text", @@ -191,7 +195,7 @@ def image_round_corner(self, **kwargs) -> list[types.TextContent]: radius_y = radius_x func = f"roundPic/radiusx/{radius_x}/radiusy/{radius_y}" - object_url = utils.url_add_processing_func(object_url, func) + object_url = utils.url_add_processing_func(auth=self.auth, url=object_url, func=func) return [ types.TextContent( type="text", @@ -228,7 +232,7 @@ def image_info(self, **kwargs) -> list[types.TextContent]: ] func = "imageInfo" - object_url = utils.url_add_processing_func(object_url, func) + object_url = utils.url_add_processing_func(auth=self.auth, url=object_url, func=func) return [ types.TextContent( type="text", @@ -259,8 +263,8 @@ def get_fop_status(self, **kwargs) -> list[types.TextContent]: return [types.TextContent(type="text", text=str(status))] -def register_tools(cli: MediaProcessingService): - tool_impl = _ToolImpl(cli) +def register_tools(cfg: config.Config, cli: MediaProcessingService): + tool_impl = _ToolImpl(cfg, cli) tools.auto_register_tools( [ tool_impl.image_scale_by_percent, diff --git a/src/mcp_server/core/media_processing/utils.py b/src/mcp_server/core/media_processing/utils.py index db43bcf..1752f78 100644 --- a/src/mcp_server/core/media_processing/utils.py +++ b/src/mcp_server/core/media_processing/utils.py @@ -1,11 +1,16 @@ +import time from urllib import parse +import qiniu + +from mcp_server.core.storage.tools import logger + FUNC_POSITION_NONE = "none" FUNC_POSITION_PREFIX = "prefix" FUNC_POSITION_SUFFIX = "suffix" -def url_add_processing_func(url: str, func: str) -> str: +def url_add_processing_func(auth: qiniu.auth, url: str, func: str) -> str: func_items = func.split("/") func_prefix = func_items[0] @@ -14,7 +19,8 @@ def url_add_processing_func(url: str, func: str) -> str: new_query = parse.quote(new_query, safe='&=') url_info = url_info._replace(query=new_query) new_url = parse.urlunparse(url_info) - return str(new_url) + new_url = _sign_url(str(new_url), auth) + return new_url def _query_add_processing_func(query: str, func: str, func_prefix: str) -> str: @@ -70,3 +76,33 @@ def _query_add_processing_func(query: str, func: str, func_prefix: str) -> str: func = first_query + func.removeprefix(func_prefix) queries.insert(0, func) return "&".join(queries) + + +def _sign_url(url: str, auth: qiniu.auth) -> str: + url_info = parse.urlparse(url) + query = url_info.query + if (not 'e=' in query) or ('token' not in query): + return url + + queries = query.split("&") + if '' in queries: + queries.remove('') + + # 移除之前的签名信息,但顺序不可变 + expires = 3600 + new_queries = [] + for queryItem in queries: + if queryItem.startswith('e='): + try: + deadline = int(queryItem.removeprefix('e=')) + expires = deadline - int(time.time()) + except Exception as e: + logger.warning(f"expires parse fail for url:{url}") + expires = 3600 + elif not queryItem.startswith('token='): + new_queries.append(queryItem) + + new_query = "&".join(new_queries) + url_info = url_info._replace(query=new_query) + new_url = parse.urlunparse(url_info) + return auth.private_download_url(new_url, expires=expires) From de033b6c48721d7b385bd4c6408e778f7a97c1cd Mon Sep 17 00:00:00 2001 From: YangSen-qn Date: Sun, 27 Apr 2025 17:19:34 +0800 Subject: [PATCH 2/5] chore: version to 1.2.1 --- CHANGELOG.md | 3 +++ pyproject.toml | 2 +- src/mcp_server/core/version/version.py | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6e14c4..0b8c6f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# v1.2.1 +- 处理多媒体签名问题 + # v1.2.0 - 支持文件上传至七牛 Bucket diff --git a/pyproject.toml b/pyproject.toml index adc8280..f27100c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "qiniu-mcp-server" -version = "1.2.0" +version = "1.2.1" description = "A MCP server project of Qiniu." requires-python = ">=3.12" authors = [ diff --git a/src/mcp_server/core/version/version.py b/src/mcp_server/core/version/version.py index 4c0f853..d2eac09 100644 --- a/src/mcp_server/core/version/version.py +++ b/src/mcp_server/core/version/version.py @@ -1,2 +1,2 @@ -VERSION = '1.2.0' \ No newline at end of file +VERSION = '1.2.1' \ No newline at end of file From c7330a0886df121cc1cc01b5b175bd46d71a6285 Mon Sep 17 00:00:00 2001 From: YangSen-qn Date: Sun, 27 Apr 2025 17:24:10 +0800 Subject: [PATCH 3/5] fix: ruff --- src/mcp_server/core/media_processing/utils.py | 14 +++++++------- uv.lock | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/mcp_server/core/media_processing/utils.py b/src/mcp_server/core/media_processing/utils.py index 1752f78..9e7fda1 100644 --- a/src/mcp_server/core/media_processing/utils.py +++ b/src/mcp_server/core/media_processing/utils.py @@ -81,7 +81,7 @@ def _query_add_processing_func(query: str, func: str, func_prefix: str) -> str: def _sign_url(url: str, auth: qiniu.auth) -> str: url_info = parse.urlparse(url) query = url_info.query - if (not 'e=' in query) or ('token' not in query): + if ('e=' not in query) or ('token=' not in query): return url queries = query.split("&") @@ -91,16 +91,16 @@ def _sign_url(url: str, auth: qiniu.auth) -> str: # 移除之前的签名信息,但顺序不可变 expires = 3600 new_queries = [] - for queryItem in queries: - if queryItem.startswith('e='): + for query_item in queries: + if query_item.startswith('e='): try: - deadline = int(queryItem.removeprefix('e=')) + deadline = int(query_item.removeprefix('e=')) expires = deadline - int(time.time()) except Exception as e: - logger.warning(f"expires parse fail for url:{url}") + logger.warning(f"expires parse fail for url:{url} err:{str(e)}") expires = 3600 - elif not queryItem.startswith('token='): - new_queries.append(queryItem) + elif not query_item.startswith('token='): + new_queries.append(query_item) new_query = "&".join(new_queries) url_info = url_info._replace(query=new_query) diff --git a/uv.lock b/uv.lock index f15b1e8..e32f988 100644 --- a/uv.lock +++ b/uv.lock @@ -687,7 +687,7 @@ wheels = [ [[package]] name = "qiniu-mcp-server" -version = "1.2.0" +version = "1.2.1" source = { editable = "." } dependencies = [ { name = "aioboto3" }, From 8db9ebc55328eff76998bd8e2458c4cd0a306741 Mon Sep 17 00:00:00 2001 From: YangSen-qn Date: Wed, 14 May 2025 13:49:21 +0800 Subject: [PATCH 4/5] fit gpt --- CHANGELOG.md | 3 +++ src/mcp_server/core/cdn/tools.py | 12 +----------- src/mcp_server/core/media_processing/tools.py | 10 +--------- src/mcp_server/core/version/version.py | 2 +- 4 files changed, 6 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b8c6f5..95c19b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# v1.2.2 +- 适配 GPT 4 大模型 + # v1.2.1 - 处理多媒体签名问题 diff --git a/src/mcp_server/core/cdn/tools.py b/src/mcp_server/core/cdn/tools.py index 15b5b28..5794ecc 100644 --- a/src/mcp_server/core/cdn/tools.py +++ b/src/mcp_server/core/cdn/tools.py @@ -106,17 +106,7 @@ def prefetch_urls(self, **kwargs) -> list[types.TextContent]: "maxItems": 10, "description": "List of directory patterns to refresh (max 10 items). Must end with '/' or '/*' to indicate directory scope", }, - }, - "anyOf": [ # 至少有一个是非空数组 - { - "required": ["urls"], - "properties": {"urls": {"not": {"maxItems": 0}}}, - }, - { - "required": ["dirs"], - "properties": {"dirs": {"not": {"maxItems": 0}}}, - }, - ], + } }, ) ) diff --git a/src/mcp_server/core/media_processing/tools.py b/src/mcp_server/core/media_processing/tools.py index 672c2f1..4939e6d 100644 --- a/src/mcp_server/core/media_processing/tools.py +++ b/src/mcp_server/core/media_processing/tools.py @@ -98,11 +98,7 @@ def image_scale_by_percent( "minimum": 1 }, }, - "required": ["object_url"], - "anyOf": [ - {"required": ["width"]}, - {"required": ["height"]} - ] + "required": ["object_url"] }, ) ) @@ -162,10 +158,6 @@ def image_scale_by_size( }, }, "required": ["object_url"], - "anyOf": [ - {"required": ["radius_x"]}, - {"required": ["radius_y"]} - ] } ) ) diff --git a/src/mcp_server/core/version/version.py b/src/mcp_server/core/version/version.py index d2eac09..a6f3572 100644 --- a/src/mcp_server/core/version/version.py +++ b/src/mcp_server/core/version/version.py @@ -1,2 +1,2 @@ -VERSION = '1.2.1' \ No newline at end of file +VERSION = '1.2.2' \ No newline at end of file From fd70516ac62dd15a2459cf473fb5eec192a07e86 Mon Sep 17 00:00:00 2001 From: YangSen-qn Date: Wed, 14 May 2025 14:03:49 +0800 Subject: [PATCH 5/5] version to 1.2.2 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f27100c..4fafedf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "qiniu-mcp-server" -version = "1.2.1" +version = "1.2.2" description = "A MCP server project of Qiniu." requires-python = ">=3.12" authors = [