mcp-server案例分享-即梦AI文生视频

1.前言

MCP Server(模型上下文协议服务器)是一种基于模型上下文协议(Model Context Protocol,简称MCP)构建的轻量级服务程序,旨在实现大型语言模型(LLM)与外部资源之间的高效、安全连接。MCP协议由Anthropic公司于2024年11月开源,其核心目标是解决AI应用中数据分散、接口不统一等问题,为开发者提供标准化的接口,使AI模型能够灵活访问本地资源和远程服务,从而提升AI助手的响应质量和工作效率。

MCP Server 的架构与工作原理

MCP Server 采用客户端-服务器(Client-Server)架构,其中客户端(MCP Client)负责与服务器建立连接,发起请求,而服务器端则处理请求并返回响应。这种架构确保了数据交互的高效性与安全性。例如,客户端可以向服务器发送请求,如“查询数据库中的某个记录”或“调用某个API”,而服务器则根据请求类型,调用相应的资源或工具,完成任务并返回结果。

MCP Server 支持动态发现和实时更新机制。例如,当新的资源或工具被添加到服务器时,客户端可以自动感知并使用这些新功能,从而提高系统的灵活性和扩展性

MCP Server 的主要功能

  1. 资源暴露与工具提供
    MCP Server 可以将本地文件、数据库、API等资源作为数据实体暴露给AI模型,同时提供工具功能,帮助AI完成复杂任务,如数据检索、内容生成、实时更新等。例如,它支持对MySQL、PostgreSQL等数据库的查询和操作,也支持对本地文件系统的读写和目录管理。
  2. 会话管理与动态通知
    MCP Server 能够管理客户端与服务器的连接,确保会话的时效性和稳定性,同时通过实时推送机制,将最新的资源信息及时传递给AI模型,以保证数据的准确性和实时性。
  3. 安全性与隐私保护
    MCP Server 采用加密认证和访问控制机制,确保数据传输的安全性,避免敏感信息泄露。例如,它支持本地运行,避免将敏感数据上传至第三方平台,从而保护用户隐私。
  4. 标准化与模块化
    MCP Server 提供了标准化的通信协议,支持两种传输协议(STDIO和SSE),并允许开发者通过插件扩展功能,使其具备灵活性和扩展性。例如,它支持通过HTTP标准POST请求与客户端进行交互,同时支持WebSocket实现实时数据推送。
  5. 多场景应用
    MCP Server 可以应用于多种场景,包括但不限于:
    • 本地资源集成:如文件操作、数据库管理、API调用等。
    • 云服务交互:如与GitHub、Slack、Google Drive等云服务的集成。
    • AI助手扩展:如为ChatGPT等AI助手提供上下文支持和工具调用能力

目前mcp-server发展速度非常快。在短短1个多月的时间目前mcp-server已经发展超过5000个mcp-server

image-20250401224117436

前端时间也给大家分享过一个速来围观!vs code + cline 联手 MCP-server,解锁大模型万物互联新玩法! 使用cline 实现mysql数据库的mcp-server 的一个案例。之前的这个案例主要介绍了如何使用这个mcp-server。今天给大家带来的是我们基于一个叫做fastapi_mcp的一个框架实现即梦AI 文生视频的一个mcp-server.那么话不多说下面带大家实现这个MCP- server.

我们这里使用Cherry Studio实现这个文生视频的mcp-server

image-20250401224556453

上图中我们输入需要调用的工具名称,提示词 以及需要调用文生视频mcp-server apikey后 后端模型通过意图识别判断调用了这个文生视频的mcp-server从而实现了调用即梦AI 创建一个文生视频。返回的视频链接我们可以在即梦AI 看到预览

image-20250401224837503

2.MCP Server 制作

在制作这个MCP Server之前我们首选介绍一下fastapi_mcp,它的实现fastapi的mcp接口实现。项目的源码地址是

https://github.com/tadata-org/fastapi_mcp

它的主要实现参考下面的代码

from fastapi import FastAPI
from fastapi_mcp import add_mcp_server

app = FastAPI()

mcp_server = add_mcp_server(
    app,                                    # Your FastAPI app
    mount_path="/mcp",                      # Where to mount the MCP server
    name="My API MCP",                      # Name for the MCP server
    describe_all_responses=True,            # False by default. Include all possible response schemas in tool descriptions, instead of just the successful response.
    describe_full_response_schema=True      # False by default. Include full JSON schema in tool descriptions, instead of just an LLM-friendly response example.
)

# Optionally add custom tools in addition to existing APIs.
@mcp_server.tool()
async def get_server_time() -> str:
    """Get the current server time."""
    from datetime import datetime
    return datetime.now().isoformat()

通过上面的函数就将我们之前对外暴露的fastap 服务转换成支持mcp -server了

我们的即梦AI文生视频的mcp -server 主要功能如下:

1. 视频生成服务

该服务通过调用极梦(剪映)的API来生成AI视频,主要功能点包括:

  • 视频生成接口 :提供 /jimeng/generate_video/ 接口,接收文本提示词、视频宽高比、时长和帧率等参数,生成对应的AI视频
  • 认证机制 :实现了基于Bearer Token的认证系统,通过 verify_auth_token 函数验证请求的合法性
  • 视频处理流程 :
    • 调用极梦API生成视频
    • 轮询检查视频生成状态
    • 下载生成的视频到本地临时存储
    • 上传视频到腾讯云COS对象存储
    • 返回视频URL和预览信息

2. MCP工具集成

服务集成了FastAPI MCP(Model Control Protocol)框架,提供了可被其他服务调用的工具:

  • MCP服务器配置 :通过 add_mcp_server 将服务注册为MCP服务
  • 视频生成工具 :提供 generate_video_mcp 工具,可以被其他支持MCP协议的服务(如AI助手)调用

3. 辅助功能

  • 文件管理 :
    • 生成带时间戳的唯一文件名
    • 下载视频到本地
    • 上传视频到腾讯云COS
    • 清理临时文件
  • 配置管理 :从配置文件读取API密钥、存储路径等信息
  • 日志记录 :详细记录API调用、视频生成过程和错误信息

4. 错误处理

  • 完善的异常处理机制,包括API调用失败、视频生成超时、文件处理错误等情况
  • 返回标准化的HTTP错误响应

技术特点

  • 使用FastAPI构建高性能异步API
  • 集成腾讯云COS对象存储服务
  • 实现MCP协议支持,便于与AI系统集成
  • 完善的日志和错误处理机制
    这个服务主要用于根据文本提示词生成AI视频,并提供标准化的接口供其他系统调用。

以上这个服务代码主要是基于我之前写的即梦AI 文生视频fastapi 服务端接口服务。主体功能还是原来的代码逻辑只是增加了一下上面代码中fastapi_mcp、@mcp_server.tool() 从而实现fastapi_mcp功能。

改造后的代码如下:

jimeng_video_service.py

from fastapi import FastAPI, HTTPException,Depends, Header
from pydantic import BaseModel
import logging
import time
import requests
import uuid
import configparser
import json
import os
import datetime
import random
from qcloud_cos import CosConfig
from qcloud_cos import CosS3Client
from fastapi_mcp import add_mcp_server

# 设置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

app = FastAPI(
    title="Jimeng Video Service API",
    description="一个用于生成AI视频的服务 API",
    version="1.0.0",
)

# 读取配置文件
config = configparser.ConfigParser()
# 在读取配置文件部分添加 COS 配置
config.read('f:\\work\\code\\2024pythontest\\jimeng\\config.ini', encoding='utf-8')

# 添加 COS 配置读取
region = config.get('common', 'region')
secret_id = config.get('common', 'secret_id')
secret_key = config.get('common', 'secret_key')
bucket = config.get('common', 'bucket')
# 在读取 video_output_path 后添加目录检查和创建逻辑
video_output_path = config.get('common', 'video_output_path')
if not os.path.exists(video_output_path):
    os.makedirs(video_output_path)
    logger.info(f"创建视频输出目录: {video_output_path}")

class VideoRequest(BaseModel):
    prompt: str
    aspect_ratio: str = "16:9"
    duration_ms: int = 5000
    fps: int = 24

def verify_auth_token(authorization: str = Header(None)):
    """验证 Authorization Header 中的 Bearer Token"""
    if not authorization:
        raise HTTPException(status_code=401, detail="Missing Authorization Header")
    
    scheme, _, token = authorization.partition(" ")
    if scheme.lower() != "bearer":
        raise HTTPException(status_code=401, detail="Invalid Authorization Scheme")
    
    # 从配置文件读取有效token列表
    valid_tokens = json.loads(config.get('auth', 'valid_tokens'))
    if token not in valid_tokens:
        raise HTTPException(status_code=403, detail="Invalid or Expired Token")
    
    return token

# 修改视频生成接口,添加鉴权依赖
# 添加新的辅助函数
def generate_timestamp_filename_for_video(extension='mp4'):
    timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
    random_number = random.randint(1000, 9999)
    filename = f"video_{timestamp}_{random_number}.{extension}"
    return filename

def download_video(url, output_path):
    response = requests.get(url, stream=True)
    response.raise_for_status()
    
    filename = generate_timestamp_filename_for_video()
    file_path = os.path.join(output_path, filename)
    
    with open(file_path, 'wb') as file:
        for chunk in response.iter_content(chunk_size=8192):
            if chunk:
                file.write(chunk)
    
    return filename, file_path

def upload_to_cos(region, secret_id, secret_key, bucket, file_name, base_path):
    config = CosConfig(
        Region=region,
        SecretId=secret_id,
        SecretKey=secret_key
    )
    client = CosS3Client(config)
    file_path = os.path.join(base_path, file_name)
    
    response = client.upload_file(
        Bucket=bucket,
        LocalFilePath=file_path,
        Key=file_name,
        PartSize=10,
        MAXThread=10,
        EnableMD5=False
    )
    
    if response['ETag']:
        url = f"https://{bucket}.cos.{region}.myqcloud.com/{file_name}"
        return url
    return None

# 修改 generate_video 函数中的返回部分
@app.post("/jimeng/generate_video/")
async def generate_video(request: VideoRequest, auth_token: str = Depends(verify_auth_token)):
    try:
        logger.info(f"generate_video API 调用开始,提示词: {request.prompt}")
        start_time = time.time()
        
        # 从配置文件中获取视频API相关配置
        video_api_cookie = config.get('video_api', 'cookie')
        video_api_sign = config.get('video_api', 'sign')
        
        # 初始化视频生成API相关配置
        video_api_headers = {
            'accept': 'application/json, text/plain, */*',
            'accept-language': 'zh-CN,zh;q=0.9',
            'app-sdk-version': '48.0.0',
            'appid': '513695',
            'appvr': '5.8.0',
            'content-type': 'application/json',
            'cookie': video_api_cookie,
            'device-time': str(int(time.time())),
            'lan': 'zh-Hans',
            'loc': 'cn',
            'origin': 'https://jimeng.jianying.com',
            'pf': '7',
            'priority': 'u=1, i',
            'referer': 'https://jimeng.jianying.com/ai-tool/video/generate',
            'sec-ch-ua': '"Google Chrome";v="129", "Not=A?Brand";v="8", "Chromium";v="129"',
            'sec-ch-ua-mobile': '?0',
            'sec-ch-ua-platform': '"Windows"',
            'sec-fetch-dest': 'empty',
            'sec-fetch-mode': 'cors',
            'sec-fetch-site': 'same-origin',
            'sign': video_api_sign,
            'sign-ver': '1',
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36'
        }
        video_api_base = "https://jimeng.jianying.com/mweb/v1"
        
        # 生成唯一的submit_id
        submit_id = str(uuid.uuid4())
        
        # 准备请求数据
        generate_video_payload = {
            "submit_id": submit_id,
            "task_extra": "{\"promptSource\":\"custom\",\"originSubmitId\":\"0340110f-5a94-42a9-b737-f4518f90361f\",\"isDefaultSeed\":1,\"originTemplateId\":\"\",\"imageNameMapping\":{},\"isUseAiGenPrompt\":false,\"batchNumber\":1}",
            "http_common_info": {"aid": 513695},
            "input": {
                "video_aspect_ratio": request.aspect_ratio,
                "seed": 2934141961,
                "video_gen_inputs": [
                    {
                        "prompt": request.prompt,
                        "fps": request.fps,
                        "duration_ms": request.duration_ms,
                        "video_mode": 2,
                        "template_id": ""
                    }
                ],
                "priority": 0,
                "model_req_key": "dreamina_ic_generate_video_model_vgfm_lite"
            },
            "mode": "workbench",
            "history_option": {},
            "commerce_info": {
                "resource_id": "generate_video",
                "resource_id_type": "str",
                "resource_sub_type": "aigc",
                "benefit_type": "basic_video_operation_vgfm_lite"
            },
            "client_trace_data": {}
        }

        # 发送生成视频请求
        generate_video_url = f"{video_api_base}/generate_video?aid=513695"
        logger.info(f"发送视频生成请求...")
        
        response = requests.post(generate_video_url, headers=video_api_headers, json=generate_video_payload)
        if response.status_code != 200:
            raise HTTPException(status_code=500, detail=f"视频生成请求失败,状态码:{response.status_code}")
            
        response_data = response.json()
        if not response_data or "data" not in response_data or "aigc_data" not in response_data["data"]:
            raise HTTPException(status_code=500, detail="视频生成接口返回格式错误")
            
        task_id = response_data["data"]["aigc_data"]["task"]["task_id"]
        logger.info(f"视频生成任务已创建,任务ID: {task_id}")
        
        # 轮询检查视频生成状态
        mget_generate_task_url = f"{video_api_base}/mget_generate_task?aid=513695"
        mget_generate_task_payload = {"task_id_list": [task_id]}
        
        # 最多尝试30次,每次间隔2秒
        for attempt in range(30):
            time.sleep(2)
            logger.info(f"检查视频状态,第 {attempt + 1} 次尝试...")
            
            response = requests.post(mget_generate_task_url, headers=video_api_headers, json=mget_generate_task_payload)
            if response.status_code != 200:
                logger.warning(f"状态检查失败,状态码:{response.status_code}")
                continue
            
            response_data = response.json()
            if not response_data or "data" not in response_data or "task_map" not in response_data["data"]:
                logger.warning("状态检查返回格式错误")
                continue
            
            task_data = response_data["data"]["task_map"].get(task_id)
            if not task_data:
                logger.warning(f"未找到任务 {task_id} 的状态信息")
                continue
            
            task_status = task_data.get("status")
            logger.info(f"任务状态: {task_status}")
            
            if task_status == 50:  # 视频生成完成
                if "item_list" in task_data and task_data["item_list"] and "video" in task_data["item_list"][0]:
                    video_data = task_data["item_list"][0]["video"]
                    if "transcoded_video" in video_data and "origin" in video_data["transcoded_video"]:
                        video_url = video_data["transcoded_video"]["origin"]["video_url"]
                        elapsed_time = time.time() - start_time
                        logger.info(f"视频生成成功,耗时 {elapsed_time:.2f} 秒,URL: {video_url}")
                        
                        # 下载视频到本地
                        try:
                            filename, file_path = download_video(video_url, video_output_path)
                            logger.info(f"视频已下载到本地: {file_path}")
                            
                            # 上传到腾讯 COS
                            cos_url = upload_to_cos(region, secret_id, secret_key, bucket, filename, video_output_path)
                            if cos_url:
                                logger.info(f"视频已上传到 COS: {cos_url}")
                                # 删除本地文件
                                os.remove(file_path)
                                return {
                                    "video_url": cos_url,
                                    "task_id": task_id,
                                    "markdown": f"<video controls><source src='{cos_url}' type='video/mp4'>视频预览</video>"
                                }
                            else:
                                raise HTTPException(status_code=500, detail="上传视频到 COS 失败")
                        except Exception as e:
                            logger.error(f"处理视频文件失败: {str(e)}")
                            raise HTTPException(status_code=500, detail=f"处理视频文件失败: {str(e)}")

                raise HTTPException(status_code=500, detail="视频生成完成但未找到下载地址")
                
        raise HTTPException(status_code=500, detail="视频生成超时")
        
    except Exception as e:
        logger.error(f"视频生成失败: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))

# 修改 MCP 服务器配置
mcp_server = add_mcp_server(
    app,
    mount_path="/mcp",
    name="Jimeng Video MCP",
    description="集成了智能视频生成功能的 MCP 服务",
    base_url="http://localhost:8088"
)

# 添加自定义 MCP 工具
@mcp_server.tool()
async def generate_video_mcp(
    prompt: str,
    aspect_ratio: str = "16:9",
    duration_ms: int = 5000,
    fps: int = 24,
    authorization: str = Header(...)
) -> dict:
    """
    生成一个基于文本提示的 AI 视频。

    Args:
        prompt: 用于生成视频的文本提示词
        aspect_ratio: 视频宽高比,默认为 "16:9"
        duration_ms: 视频时长(毫秒),默认为 5000
        fps: 视频帧率,默认为 24
        authorization: Bearer token 用于认证(必填)

    Returns:
        dict: 包含以下字段的字典:
            - video_url: 生成视频的 URL
            - task_id: 任务 ID
            - markdown: 视频预览的 markdown 代码
    """
    request = VideoRequest(
        prompt=prompt,
        aspect_ratio=aspect_ratio,
        duration_ms=duration_ms,
        fps=fps
    )
    return await generate_video(request, auth_token=verify_auth_token(authorization))

if __name__ == "__main__":
    import uvicorn
    # 修改启动配置
    uvicorn.run(
        app,
        host="0.0.0.0",
        port=8088,
        log_level="info",
        reload=False  # 禁用热重载以避免初始化问题
    )

上述代码编写完成后我们启动它

3.MCP Server 运行

点击trae run python file 完成 服务端启动

image-20250401230046029

当然你也可以使用python jimeng_video_service.py 启动这个服务

上述启动和我们普通的fastapi 服务端启动类似。启动后他对外提供一个叫做http://localhost:8088/mcp sse服务

4 .Cherry Studio 配置mcp server

我们下载最新的Cherry Studio,这里为什么下载最新的 我记得早期1.0之前的版本好像是不支持mcp server的 新版本是支持的,支持不支持大家可以看这个

image-20250401231814428

点开这里,我们添加一个服务器,选择SSE

image-20250401231911717

我们在URL 填写上面创建的SSE服务器地址http://localhost:8088/mcp 点击保存按钮完成设置。这里点击保存后,客户端Cherry Studio会想服务器发起请求。

image-20250401232055923

看到这个就说明客户端和服务器之间产生通讯。

5 .Cherry Studio 调用mcp server

这里我们选择一个模型支持function call 的模型,怎么判断呢模型设置里面有个小工具按钮,我们这里拿火山引擎提供的deepseek-V3模型作为案例

image-20250401232402067

打开一个聊天对话窗口 选择火山引擎提供的deepseek-V3

image-20250401232533211

选择模型后下面聊天窗口就会多出MCP Server小窗口,我们选择开启MCP服务器并开启我们需要的文生视频MCPserver

image-20250401232631608

以上设置完成后,我们就可以进入聊天对话界面了。为了方便测试我们输入以下提示词

请帮我调用即梦AI文生视频mcpserver 工具,用户输入的提示词“小马过河”,需要的authorization 为”bearer sk-zhouhui1122444”

因为mcpserver 需要2个必填参数 一个是提示词,一个是鉴权,所以这2个值需要用户输入。当然你也可以一个一个填写通过模型引导你填入。我这里偷懒就直接把需要的2个参考一并告诉模型,这样它就不需要和客户交互直接执行函数调用了。怎么查看必填项呢

我们可以在之前的mcpserver 设置查看到

image-20250401233034871

这2个必填项主要是服务端代码来实现的。

image-20250401233113844

填写提示词后 deepseek-v3模型会通过意图识别自动开启函数调用

image-20250401233212138

在调用过程中我们也可以看后端程序的运行,直到后面返回结果给前端

image-20250401233517227

image-20250401233559711

这里比较遗憾的是cherry studio 不知道怎么实现视频的预览,所以需要我们把生成的视频URL 链接复制到浏览器上重新下载才能看到效果。

总结

总体来说实现这个mcp server 难度不大,主要还是借助了fastapi_mcp 以及sse方式实现了这个文生视频的MCP server。感兴趣小伙伴可以基于这块框架把我之前写的fastapi 服务端接口一并改成支持mcp server服务,通过改造几个程序大家更容易理解和掌握目前最流行的

MCP server了,今天的分享就到这里结束了,感兴趣的小伙伴可以关注支持,我们下个文章见。

### MCP MySQL Server Configuration and Management #### Overview of MCP MySQL Server Setup For configuring an MCP (Multi-Cloud Platform) environment with a MySQL server, the setup involves ensuring that all necessary configurations are correctly applied to integrate MySQL as part of the data storage solution. In scenarios where applications like Nacos need to connect to a MySQL database, it is crucial to ensure proper initialization and configuration. #### Initialization of MySQL Database for Application Integration When initializing a MySQL database specifically for application integration such as switching from default settings to using MySQL, one can modify `application.properties` or configure during Docker startup[^1]. This ensures that the application connects to the correct MySQL instance configured within the MCP environment. #### Schema Creation for Application Data Persistence To avoid issues related to no datasource exceptions when deploying services on platforms like Nacos via Docker while aiming at persisting data into MySQL, creating databases and tables beforehand is essential. The SQL script located at `D:\software\nacos\nacos-server-2.3.1\conf\mysql-schema.sql` serves this purpose by providing commands needed for setting up schemas required by Nacos[^2]. #### Troubleshooting Common Issues During Deployment In cases similar to those described in experiences involving deployment challenges—such as encountering errors due to missing database creation steps—it becomes evident how critical these preparatory actions are before attempting service deployments. Ensuring that both the database exists along with its schema helps prevent common pitfalls associated with connectivity problems between containers running inside Docker environments and external databases[^3]. ```sql -- Example command to create a new database named 'mcp_db' CREATE DATABASE mcp_db; USE mcp_db; SOURCE D:/software/nacos/nacos-server-2.3.1/conf/mysql-schema.sql; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值