欢迎光临
我们一直在努力

第四章 FastMCP 服务器框架之自定义HTTP路由

目录

  • 简介
  • custom_route装饰器详解
  • Route对象创建与请求处理
  • 典型应用场景实现
  • 与MCP核心协议的协同工作
  • API文档生成控制
  • 简介

    本文档详细讲解FastMCP框架中custom_route装饰器的使用方法,说明如何在MCP服务器中集成自定义HTTP端点。通过分析auth/routes.py中的认证端点实现,阐述自定义路由与MCP核心协议的协同工作模式,并提供OAuth回调、健康检查、管理API等典型应用场景的实现示例。

    本节来源

    • server.py

    custom_route装饰器详解

    custom_route装饰器是FastMCP服务器提供的核心功能之一,用于在MCP协议之外注册自定义的HTTP路由。该装饰器允许开发者添加任意HTTP端点,适用于OAuth回调、健康检查、管理API等场景。

    装饰器接受以下参数:

    • path: 路由的URL路径(如"/oauth/callback")
    • methods: 支持的HTTP方法列表(如[“GET”, “POST”])
    • name: 路由的可选名称(用于Starlette的反向URL查找功能)
    • include_in_schema: 是否包含在OpenAPI架构中,默认为True

    使用custom_route装饰器时,处理函数必须是异步函数,接收Starlette的Request对象并返回Response对象。

    本节来源

    • server.py

    Route对象创建与请求处理

    当使用custom_route装饰器时,系统会创建一个Route对象并将其添加到服务器的自定义路由列表中。Route对象包含路径、端点处理函数、支持的方法、名称和架构包含设置等信息。

    请求处理函数的签名必须严格遵循async def handler(request: Request) -> Response的模式。处理函数可以访问请求的完整信息,包括查询参数、请求体、头部等,并可以返回各种类型的响应,如JSON响应、文本响应或自定义响应。

    #mermaid-svg-9vO3JgzC8E0i6Xaq {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-9vO3JgzC8E0i6Xaq .error-icon{fill:#552222;}#mermaid-svg-9vO3JgzC8E0i6Xaq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9vO3JgzC8E0i6Xaq .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-9vO3JgzC8E0i6Xaq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9vO3JgzC8E0i6Xaq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9vO3JgzC8E0i6Xaq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9vO3JgzC8E0i6Xaq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9vO3JgzC8E0i6Xaq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9vO3JgzC8E0i6Xaq .marker.cross{stroke:#333333;}#mermaid-svg-9vO3JgzC8E0i6Xaq svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9vO3JgzC8E0i6Xaq .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-9vO3JgzC8E0i6Xaq .cluster-label text{fill:#333;}#mermaid-svg-9vO3JgzC8E0i6Xaq .cluster-label span{color:#333;}#mermaid-svg-9vO3JgzC8E0i6Xaq .label text,#mermaid-svg-9vO3JgzC8E0i6Xaq span{fill:#333;color:#333;}#mermaid-svg-9vO3JgzC8E0i6Xaq .node rect,#mermaid-svg-9vO3JgzC8E0i6Xaq .node circle,#mermaid-svg-9vO3JgzC8E0i6Xaq .node ellipse,#mermaid-svg-9vO3JgzC8E0i6Xaq .node polygon,#mermaid-svg-9vO3JgzC8E0i6Xaq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9vO3JgzC8E0i6Xaq .node .label{text-align:center;}#mermaid-svg-9vO3JgzC8E0i6Xaq .node.clickable{cursor:pointer;}#mermaid-svg-9vO3JgzC8E0i6Xaq .arrowheadPath{fill:#333333;}#mermaid-svg-9vO3JgzC8E0i6Xaq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9vO3JgzC8E0i6Xaq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9vO3JgzC8E0i6Xaq .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-9vO3JgzC8E0i6Xaq .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-9vO3JgzC8E0i6Xaq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9vO3JgzC8E0i6Xaq .cluster text{fill:#333;}#mermaid-svg-9vO3JgzC8E0i6Xaq .cluster span{color:#333;}#mermaid-svg-9vO3JgzC8E0i6Xaq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9vO3JgzC8E0i6Xaq :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    匹配自定义路由

    不匹配

    HTTP请求

    路径匹配

    调用处理函数

    处理请求逻辑

    生成响应

    返回Response对象

    继续其他路由匹配

    图源

    • server.py

    本节来源

    • server.py

    典型应用场景实现

    OAuth回调端点

    在OAuth认证流程中,custom_route装饰器可用于实现回调端点,处理授权服务器重定向回来的请求。

    #mermaid-svg-X0y78ZtOpVmOqGEZ {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-X0y78ZtOpVmOqGEZ .error-icon{fill:#552222;}#mermaid-svg-X0y78ZtOpVmOqGEZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-X0y78ZtOpVmOqGEZ .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-X0y78ZtOpVmOqGEZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-X0y78ZtOpVmOqGEZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-X0y78ZtOpVmOqGEZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-X0y78ZtOpVmOqGEZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-X0y78ZtOpVmOqGEZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-X0y78ZtOpVmOqGEZ .marker.cross{stroke:#333333;}#mermaid-svg-X0y78ZtOpVmOqGEZ svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-X0y78ZtOpVmOqGEZ .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-X0y78ZtOpVmOqGEZ text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-X0y78ZtOpVmOqGEZ .actor-line{stroke:grey;}#mermaid-svg-X0y78ZtOpVmOqGEZ .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-X0y78ZtOpVmOqGEZ .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-X0y78ZtOpVmOqGEZ #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-X0y78ZtOpVmOqGEZ .sequenceNumber{fill:white;}#mermaid-svg-X0y78ZtOpVmOqGEZ #sequencenumber{fill:#333;}#mermaid-svg-X0y78ZtOpVmOqGEZ #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-X0y78ZtOpVmOqGEZ .messageText{fill:#333;stroke:#333;}#mermaid-svg-X0y78ZtOpVmOqGEZ .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-X0y78ZtOpVmOqGEZ .labelText,#mermaid-svg-X0y78ZtOpVmOqGEZ .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-X0y78ZtOpVmOqGEZ .loopText,#mermaid-svg-X0y78ZtOpVmOqGEZ .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-X0y78ZtOpVmOqGEZ .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-X0y78ZtOpVmOqGEZ .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-X0y78ZtOpVmOqGEZ .noteText,#mermaid-svg-X0y78ZtOpVmOqGEZ .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-X0y78ZtOpVmOqGEZ .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-X0y78ZtOpVmOqGEZ .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-X0y78ZtOpVmOqGEZ .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-X0y78ZtOpVmOqGEZ .actorPopupMenu{position:absolute;}#mermaid-svg-X0y78ZtOpVmOqGEZ .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-X0y78ZtOpVmOqGEZ .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-X0y78ZtOpVmOqGEZ .actor-man circle,#mermaid-svg-X0y78ZtOpVmOqGEZ line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-X0y78ZtOpVmOqGEZ :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    "客户端应用"

    "MCP服务器"

    "授权服务器"

    重定向到授权端点

    显示登录页面

    提交凭证

    重定向到回调URL

    GET /oauth/callback

    处理回调请求

    重定向到应用

    "客户端应用"

    "MCP服务器"

    "授权服务器"

    图源

    • auth_server.py

    健康检查端点

    健康检查是微服务架构中的重要组成部分,custom_route装饰器可以轻松实现健康检查端点。

    @server.custom_route("/health", methods=["GET"])
    async def health_check(request: Request) –> Response:
    return JSONResponse({"status": "ok"})

    管理API端点

    管理API通常需要特殊的访问控制,custom_route装饰器可以与中间件结合使用,实现安全的管理接口。

    本节来源

    • auth_server.py
    • server.py

    与MCP核心协议的协同工作

    自定义路由与MCP核心协议可以协同工作,通过auth/routes.py中的认证端点实现可以看出,自定义路由可以与MCP的认证机制无缝集成。

    在create_auth_routes函数中,多个认证相关的路由被创建,包括授权、令牌、注册和撤销端点。这些端点使用CORS中间件处理跨域请求,并与OAuth授权服务器提供者集成。

    #mermaid-svg-yGVZnnRgCUyO5JEd {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-yGVZnnRgCUyO5JEd .error-icon{fill:#552222;}#mermaid-svg-yGVZnnRgCUyO5JEd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-yGVZnnRgCUyO5JEd .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-yGVZnnRgCUyO5JEd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-yGVZnnRgCUyO5JEd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-yGVZnnRgCUyO5JEd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-yGVZnnRgCUyO5JEd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-yGVZnnRgCUyO5JEd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-yGVZnnRgCUyO5JEd .marker.cross{stroke:#333333;}#mermaid-svg-yGVZnnRgCUyO5JEd svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-yGVZnnRgCUyO5JEd g.classGroup text{fill:#9370DB;fill:#131300;stroke:none;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-yGVZnnRgCUyO5JEd g.classGroup text .title{font-weight:bolder;}#mermaid-svg-yGVZnnRgCUyO5JEd .nodeLabel,#mermaid-svg-yGVZnnRgCUyO5JEd .edgeLabel{color:#131300;}#mermaid-svg-yGVZnnRgCUyO5JEd .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-yGVZnnRgCUyO5JEd .label text{fill:#131300;}#mermaid-svg-yGVZnnRgCUyO5JEd .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-yGVZnnRgCUyO5JEd .classTitle{font-weight:bolder;}#mermaid-svg-yGVZnnRgCUyO5JEd .node rect,#mermaid-svg-yGVZnnRgCUyO5JEd .node circle,#mermaid-svg-yGVZnnRgCUyO5JEd .node ellipse,#mermaid-svg-yGVZnnRgCUyO5JEd .node polygon,#mermaid-svg-yGVZnnRgCUyO5JEd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-yGVZnnRgCUyO5JEd .divider{stroke:#9370DB;stroke:1;}#mermaid-svg-yGVZnnRgCUyO5JEd g.clickable{cursor:pointer;}#mermaid-svg-yGVZnnRgCUyO5JEd g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-yGVZnnRgCUyO5JEd g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-yGVZnnRgCUyO5JEd .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-yGVZnnRgCUyO5JEd .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-yGVZnnRgCUyO5JEd .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-yGVZnnRgCUyO5JEd .dashed-line{stroke-dasharray:3;}#mermaid-svg-yGVZnnRgCUyO5JEd #compositionStart,#mermaid-svg-yGVZnnRgCUyO5JEd .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-yGVZnnRgCUyO5JEd #compositionEnd,#mermaid-svg-yGVZnnRgCUyO5JEd .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-yGVZnnRgCUyO5JEd #dependencyStart,#mermaid-svg-yGVZnnRgCUyO5JEd .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-yGVZnnRgCUyO5JEd #dependencyStart,#mermaid-svg-yGVZnnRgCUyO5JEd .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-yGVZnnRgCUyO5JEd #extensionStart,#mermaid-svg-yGVZnnRgCUyO5JEd .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-yGVZnnRgCUyO5JEd #extensionEnd,#mermaid-svg-yGVZnnRgCUyO5JEd .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-yGVZnnRgCUyO5JEd #aggregationStart,#mermaid-svg-yGVZnnRgCUyO5JEd .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-yGVZnnRgCUyO5JEd #aggregationEnd,#mermaid-svg-yGVZnnRgCUyO5JEd .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-yGVZnnRgCUyO5JEd .edgeTerminals{font-size:11px;}#mermaid-svg-yGVZnnRgCUyO5JEd :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    "创建"

    "接收"

    "返回"

    FastMCP

    +_custom_starlette_routes : list[Route]

    +custom_route(path, methods, name, include_in_schema)

    Route

    +path : str

    +endpoint : Callable

    +methods : list[str]

    +name : str | None

    +include_in_schema : bool

    Request

    +query_params : dict

    +headers : dict

    +body : bytes

    Response

    +status_code : int

    +headers : dict

    +body : bytes

    图源

    • server.py
    • routes.py

    本节来源

    • routes.py
    • server.py

    API文档生成控制

    include_in_schema参数控制自定义路由是否包含在OpenAPI架构文档中。这对于管理API或内部端点特别有用,可以选择性地将其从公开的API文档中隐藏。

    当include_in_schema设置为False时,端点仍然可用,但不会出现在自动生成的API文档中,这为API的安全性和隐私性提供了额外的控制层。

    本节来源

    • server.py
    赞(0)
    未经允许不得转载:171主机测评 » 第四章 FastMCP 服务器框架之自定义HTTP路由
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址