Skip to content

Commit 04d71e1

Browse files
authored
add miku mcp server v0.1
feat: optimize miku MCP server - remove unused import and add stream creation API
2 parents 8ff3741 + 9287187 commit 04d71e1

File tree

5 files changed

+220
-0
lines changed

5 files changed

+220
-0
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ authors = [
88
]
99
keywords = ["qiniu", "mcp", "llm"]
1010
dependencies = [
11+
"aiohttp>=3.9.0",
1112
"aioboto3>=13.2.0",
1213
"fastjsonschema>=2.21.1",
1314
"httpx>=0.28.1",

src/mcp_server/core/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from .media_processing import load as load_media_processing
44
from .cdn import load as load_cdn
55
from .version import load as load_version
6+
from .miku import load as load_miku
67

78

89
def load():
@@ -17,4 +18,6 @@ def load():
1718
load_cdn(cfg)
1819
# 智能多媒体
1920
load_media_processing(cfg)
21+
# Miku
22+
load_miku(cfg)
2023

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from .miku import MikuService
2+
from .tools import register_tools
3+
from ...config import config
4+
5+
6+
def load(cfg: config.Config):
7+
miku = MikuService(cfg)
8+
register_tools(miku)
9+
10+
11+
__all__ = ["load"]

src/mcp_server/core/miku/miku.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import aiohttp
2+
import logging
3+
4+
from typing import Dict, Any
5+
from ...config import config
6+
from ...consts import consts
7+
8+
logger = logging.getLogger(consts.LOGGER_NAME)
9+
10+
11+
class MikuService:
12+
def __init__(self, cfg: config.Config = None):
13+
self.config = cfg
14+
self.api_key = cfg.access_key if cfg else None
15+
self.endpoint_url = cfg.endpoint_url if cfg else None
16+
17+
def _get_auth_header(self) -> Dict[str, str]:
18+
"""Generate Bearer token authorization header"""
19+
if not self.api_key:
20+
raise ValueError("QINIU_API_KEY is not configured")
21+
return {
22+
"Authorization": f"Bearer {self.api_key}"
23+
}
24+
25+
def _build_bucket_url(self, bucket: str) -> str:
26+
"""Build S3-style bucket URL"""
27+
if not self.endpoint_url:
28+
raise ValueError("QINIU_ENDPOINT_URL is not configured")
29+
30+
# Remove protocol if present in endpoint_url
31+
endpoint = self.endpoint_url
32+
if endpoint.startswith("http://"):
33+
endpoint = endpoint[7:]
34+
elif endpoint.startswith("https://"):
35+
endpoint = endpoint[8:]
36+
37+
# Build URL in format: https://<bucket>.<endpoint>
38+
return f"https://{bucket}.{endpoint}"
39+
40+
def _build_stream_url(self, bucket: str, stream: str) -> str:
41+
"""Build S3-style stream URL"""
42+
if not self.endpoint_url:
43+
raise ValueError("QINIU_ENDPOINT_URL is not configured")
44+
45+
# Remove protocol if present in endpoint_url
46+
endpoint = self.endpoint_url
47+
if endpoint.startswith("http://"):
48+
endpoint = endpoint[7:]
49+
elif endpoint.startswith("https://"):
50+
endpoint = endpoint[8:]
51+
52+
# Build URL in format: https://<bucket>.<endpoint>/<stream>
53+
return f"https://{bucket}.{endpoint}/{stream}"
54+
55+
async def create_bucket(self, bucket: str) -> Dict[str, Any]:
56+
"""
57+
Create a bucket using S3-style API
58+
59+
Args:
60+
bucket: The bucket name to create
61+
62+
Returns:
63+
Dict containing the response status and message
64+
"""
65+
url = self._build_bucket_url(bucket)
66+
headers = self._get_auth_header()
67+
68+
logger.info(f"Creating bucket: {bucket} at {url}")
69+
70+
async with aiohttp.ClientSession() as session:
71+
async with session.put(url, headers=headers) as response:
72+
status = response.status
73+
text = await response.text()
74+
75+
if status == 200 or status == 201:
76+
logger.info(f"Successfully created bucket: {bucket}")
77+
return {
78+
"status": "success",
79+
"bucket": bucket,
80+
"url": url,
81+
"message": f"Bucket '{bucket}' created successfully",
82+
"status_code": status
83+
}
84+
else:
85+
logger.error(f"Failed to create bucket: {bucket}, status: {status}, response: {text}")
86+
return {
87+
"status": "error",
88+
"bucket": bucket,
89+
"url": url,
90+
"message": f"Failed to create bucket: {text}",
91+
"status_code": status
92+
}
93+
94+
async def create_stream(self, bucket: str, stream: str) -> Dict[str, Any]:
95+
"""
96+
Create a stream using S3-style API
97+
98+
Args:
99+
bucket: The bucket name
100+
stream: The stream name to create
101+
102+
Returns:
103+
Dict containing the response status and message
104+
"""
105+
url = self._build_stream_url(bucket, stream)
106+
headers = self._get_auth_header()
107+
108+
logger.info(f"Creating stream: {stream} in bucket: {bucket} at {url}")
109+
110+
async with aiohttp.ClientSession() as session:
111+
async with session.put(url, headers=headers) as response:
112+
status = response.status
113+
text = await response.text()
114+
115+
if status == 200 or status == 201:
116+
logger.info(f"Successfully created stream: {stream} in bucket: {bucket}")
117+
return {
118+
"status": "success",
119+
"bucket": bucket,
120+
"stream": stream,
121+
"url": url,
122+
"message": f"Stream '{stream}' created successfully in bucket '{bucket}'",
123+
"status_code": status
124+
}
125+
else:
126+
logger.error(f"Failed to create stream: {stream}, status: {status}, response: {text}")
127+
return {
128+
"status": "error",
129+
"bucket": bucket,
130+
"stream": stream,
131+
"url": url,
132+
"message": f"Failed to create stream: {text}",
133+
"status_code": status
134+
}

src/mcp_server/core/miku/tools.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import logging
2+
3+
from mcp import types
4+
5+
from .miku import MikuService
6+
from ...consts import consts
7+
from ...tools import tools
8+
9+
logger = logging.getLogger(consts.LOGGER_NAME)
10+
11+
_BUCKET_DESC = "Miku bucket name"
12+
_STREAM_DESC = "Miku stream name"
13+
14+
15+
class _ToolImpl:
16+
def __init__(self, miku: MikuService):
17+
self.miku = miku
18+
19+
@tools.tool_meta(
20+
types.Tool(
21+
name="miku_create_bucket",
22+
description="Create a new bucket in Miku using S3-style API. The bucket will be created at https://<bucket>.<endpoint_url>",
23+
inputSchema={
24+
"type": "object",
25+
"properties": {
26+
"bucket": {
27+
"type": "string",
28+
"description": _BUCKET_DESC,
29+
},
30+
},
31+
"required": ["bucket"],
32+
},
33+
)
34+
)
35+
async def create_bucket(self, **kwargs) -> list[types.TextContent]:
36+
result = await self.miku.create_bucket(**kwargs)
37+
return [types.TextContent(type="text", text=str(result))]
38+
39+
@tools.tool_meta(
40+
types.Tool(
41+
name="miku_create_stream",
42+
description="Create a new stream in Miku using S3-style API. The stream will be created at https://<bucket>.<endpoint_url>/<stream>",
43+
inputSchema={
44+
"type": "object",
45+
"properties": {
46+
"bucket": {
47+
"type": "string",
48+
"description": _BUCKET_DESC,
49+
},
50+
"stream": {
51+
"type": "string",
52+
"description": _STREAM_DESC,
53+
},
54+
},
55+
"required": ["bucket", "stream"],
56+
},
57+
)
58+
)
59+
async def create_stream(self, **kwargs) -> list[types.TextContent]:
60+
result = await self.miku.create_stream(**kwargs)
61+
return [types.TextContent(type="text", text=str(result))]
62+
63+
64+
def register_tools(miku: MikuService):
65+
tool_impl = _ToolImpl(miku)
66+
tools.auto_register_tools(
67+
[
68+
tool_impl.create_bucket,
69+
tool_impl.create_stream,
70+
]
71+
)

0 commit comments

Comments
 (0)