一、背景

有时候,当mcp server部署在mcp host所在的机器是应用服务器上时,对sse服务端口又不允许开放,只能借助应用服务器的nginx转发时,会发现,mcp server默认的路由会有部分冲突,此时就可以借助nginx的配置实现路由转发,mcp server的源码中,虽然制定了sse、messages、mcp、root的路径配置,但是,只有root+messages的配置是起作用的,此时,当mcp server想要满足一定的服务前缀实现时,就会产生部分问题,该文档记录了简单的nginx配置转发和mcp server的路径配置,不多bb,直接上源代码。

二、目的

外部通过访问nginx服务所在的域名,加上该服务的固定前缀,实现nginx转发,通过sse流建立后,能够完成后续正常的数据请求,流程图如下:
mcp server custom route
通过访问: www.domain.com/api/prefix/sse 建立长链接通道,在nginx处转化为mcp host的/see接口(FastMCP Settings的sse_path,经过测试)
修改无效,所以通过nginx rewrite进行转发处理,sse建立链接后,会返回消息的uri地址(通过curl www.domain.com/api/prefix/sse 查询),测试案例
如下:
mcp server data_messages
也就是后续地址会通过data表示的地址进行数据交互,所以,修改mcp server的服务配置信息,进行地址修改,默认配置为:/messages/。修改手段有两种:
方案一:修改message_path
方案二:修改rootmessage_path
两者原理一致,最后都是通过mcp server的 _normalize_path方法获取交互数据的路径,由mcp客户端发起POST请求实现。

三、代码实操

  • MCP server端
    • mcp server config yaml
      1
      2
      3
      4
      5
      6
      # 增加messages路径配置
      # .env
      Host=0.0.0.0
      Port=9116
      MessagePath="/api/prefix/sse/messages/" # rewrite message_path
      Debug=True
    • mcp server config Settings
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      # mcp server settings
      from pydantic_settings import BaseSettings
      from dotenv import load_dotenv

      load_dotenv()


      class Settings(BaseSettings):
      Host: str
      Port: int
      Debug: bool
      MessagePath: str

      class Config:
      env_file = ".env"
      env_file_encoding = "utf-8"
    • mcp server
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      from mcp.server.fastmcp import FastMCP
      from .tools import *
      from .config import Settings


      def create_mcp():
      mcp = FastMCP(
      name="test",
      description="test",
      host=settings.Host,
      port=settings.Port,
      message_path=settings.MessagePath,
      )

      # register tools or functions
      mcp.add_tool(weather_tools)

      return mcp
  • Nginx配置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    location ~ ^/api/prefix/sse$ {
    rewrite ^/api/prefix/sse$ /sse break;
    proxy_pass MCP-Host:Port;
    proxy_http_version 1.1;
    proxy_set_header Connection '';

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $remote_addr;

    proxy_set_header tenant_id $http_tenant_id;
    proxy_set_header user_id $http_user_id;
    proxy_pass_request_headers on;

    proxy_connect_timeout 600;
    proxy_read_timeout 600;
    proxy_send_timeout 600;

    add_header Cache-Control no-cache;
    add_header Content-Type text/event-stream;
    chunked_transfer_encoding off;
    }

    location ~ ^/api/prefix/sse/messages/?$ {
    proxy_pass MCP-Host:Port;
    proxy_http_version 1.1;
    proxy_set_header Connection '';
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_pass_request_headers on;

    proxy_method POST;
    }
    该配置就是实现:/api/prefix/sse -> mcp-host:port/sse
    数据接口:/api/prefix/sse/messages -> mcp-host:port/api/prefix/sse/messages

四、MCP Tools List

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# mcp_server_tools.py  
import asyncio
from mcp.client.sse import sse_client
from mcp import ClientSession
import argparse


async def main(endpoint: str, tools_name: str = None, cookies: str = None):
headers = {}
if cookies:
headers['Cookie'] = cookies
pass

async with sse_client(endpoint, headers=headers) as (reader, writer):
async with ClientSession(reader, writer) as session:
await session.initialize()
tools_list = await session.list_tools()

has_exists = False
for tool in tools_list.tools:
if tools_name and tools_name != tool.name:
continue
print(f" 工具名称: {tool.name}")
print(f" 工具描述: {tool.description}")
print(f" 工具结构化参数: {tool.inputSchema}")
print("-----" * 5)
has_exists = True
if not has_exists:
print(f"没有找到 {tools_name} 工具")


if __name__ == '__main__':
parser = argparse.ArgumentParser(description="MCP 工具查看器")
parser.add_argument("--host", help="MCP Server Host", required=True)
parser.add_argument("--tool", help="指定要查看的工具名")
args = parser.parse_args()

cookie = None
asyncio.run(main(args.host, args.tool, cookie))

环境依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
annotated-types==0.7.0
anyio==4.9.0
attrs==25.3.0
certifi==2025.6.15
click==8.2.1
fastapi==0.115.12
h11==0.16.0
httpcore==1.0.9
httpx==0.28.1
httpx-sse==0.4.1
idna==3.10
jsonschema==4.24.0
jsonschema-specifications==2025.4.1
markdown-it-py==3.0.0
mcp==1.10.1
mdurl==0.1.2
pydantic==2.11.7
pydantic-core==2.33.2
pydantic-settings==2.10.1
pygments==2.19.2
python-dotenv==1.1.1
python-multipart==0.0.20
referencing==0.36.2
rich==14.0.0
rpds-py==0.25.1
shellingham==1.5.4
sniffio==1.3.1
sse-starlette==2.3.6
starlette==0.47.1
typer==0.16.0
typing-extensions==4.14.0
typing-inspection==0.4.1
uvicorn==0.35.0

安装依赖:

pip install -r requirements.txt

使用方法:

python mcp_sse_tool_inspector.py –host=”http://127.0.0.1:9117/sse" –tool=”say_hello”

效果如下:
mcp-tools-results