geqintan commited on
Commit
36a9cae
·
1 Parent(s): 5c47533
Files changed (3) hide show
  1. apps/app01.py +12 -12
  2. apps/app02.py +12 -12
  3. 博客.md +94 -52
apps/app01.py CHANGED
@@ -24,15 +24,15 @@ class App01:
24
  """Add two integers in App01"""
25
  return num1 + num2
26
 
27
- async def sse_endpoint(self, request: Request):
28
- print("Entering App01 SSE endpoint")
29
- try:
30
- # 这里可以实现 app01 的 SSE 逻辑
31
- yield {"event": "message", "data": "Hello from App01"}
32
- await asyncio.sleep(1) # 示例:每秒发送一条消息
33
- yield {"event": "message", "data": "Another message from App01"}
34
- except Exception as e:
35
- print(f"Error in App01 SSE endpoint: {e}")
36
- # Optionally yield an error event to the client
37
- # yield {"event": "error", "data": str(e)}
38
- print("Exiting App01 SSE endpoint")
 
24
  """Add two integers in App01"""
25
  return num1 + num2
26
 
27
+ # async def sse_endpoint(self, request: Request):
28
+ # print("Entering App01 SSE endpoint")
29
+ # try:
30
+ # # 这里可以实现 app01 的 SSE 逻辑
31
+ # yield {"event": "message", "data": "Hello from App01"}
32
+ # await asyncio.sleep(1) # 示例:每秒发送一条消息
33
+ # yield {"event": "message", "data": "Another message from App01"}
34
+ # except Exception as e:
35
+ # print(f"Error in App01 SSE endpoint: {e}")
36
+ # # Optionally yield an error event to the client
37
+ # # yield {"event": "error", "data": str(e)}
38
+ # print("Exiting App01 SSE endpoint")
apps/app02.py CHANGED
@@ -24,15 +24,15 @@ class App02:
24
  """Multiply two integers in App02"""
25
  return num1 * num2
26
 
27
- async def sse_endpoint(self, request: Request):
28
- print("Entering App02 SSE endpoint")
29
- try:
30
- # 这里可以实现 app02 的 SSE 逻辑
31
- yield {"event": "message", "data": "Hello from App02"}
32
- await asyncio.sleep(2) # 示例:每2秒发送一条消息
33
- yield {"event": "message", "data": "Another message from App02"}
34
- except Exception as e:
35
- print(f"Error in App02 SSE endpoint: {e}")
36
- # Optionally yield an error event to the client
37
- # yield {"event": "error", "data": str(e)}
38
- print("Exiting App02 SSE endpoint")
 
24
  """Multiply two integers in App02"""
25
  return num1 * num2
26
 
27
+ # async def sse_endpoint(self, request: Request):
28
+ # print("Entering App02 SSE endpoint")
29
+ # try:
30
+ # # 这里可以实现 app02 的 SSE 逻辑
31
+ # yield {"event": "message", "data": "Hello from App02"}
32
+ # await asyncio.sleep(2) # 示例:每2秒发送一条消息
33
+ # yield {"event": "message", "data": "Another message from App02"}
34
+ # except Exception as e:
35
+ # print(f"Error in App02 SSE endpoint: {e}")
36
+ # # Optionally yield an error event to the client
37
+ # # yield {"event": "error", "data": str(e)}
38
+ # print("Exiting App02 SSE endpoint")
博客.md CHANGED
@@ -5,9 +5,15 @@
5
  在当前大模型智能体快速发展的背景下,如何有效地管理和调用各种外部工具(Tool)成为了一个关键问题。Model Context Protocol (MCP) 提供了一种标准化的方式来解决这个问题。本项目旨在利用 MCP 的 Python SDK,构建一个**单端口服务,能够加载并管理多个独立的 MCP 应用**。
6
 
7
  核心目标如下:
8
- - 使用 Python SDK 实现一个 MCP 服务。
9
- - 服务运行在 **7860 端口**。
10
- - 支持通过统一的端口和不同的路径访问不同的 MCP 应用,路径格式为 `http://localhost:7860/app_name/sse`。
 
 
 
 
 
 
11
 
12
  ## 2. 技术选型与实现思路
13
 
@@ -24,77 +30,113 @@
24
  ## 3. 代码实现示例
25
 
26
  以下是 `app.py` 的核心代码示例,展示了如何使用 FastAPI 和 FastMCP 挂载多个应用:
27
-
28
  ```python
29
- import uvicorn
30
  from fastapi import FastAPI
31
- from mcp.server.fastmcp import FastMCP
32
- import mcp.types as types
33
 
34
- # 创建主 FastAPI 应用
35
- app = FastAPI(
36
- title="Multi-App MCP Server",
37
- description="A single-port server hosting multiple MCP applications",
38
- version="0.1.0",
39
- )
40
 
41
- # 创建第一个 MCP 应用实例 (app01)
42
- app01 = FastMCP("app01")
43
-
44
- # 为 app01 添加一个示例工具
45
- @app01.tool()
46
- def app01_add_integers(num1: int, num2: int) -> int:
47
- """Adds two integers"""
48
- return num1 + num2
49
-
50
- # 创建第二个 MCP 应用实例 (app02)
51
- app02 = FastMCP("app02")
52
 
53
- # app02 添加一个示例工具
54
- @app02.tool()
55
- def app02_multiply_integers(num1: int, num2: int) -> int:
56
- """Multiplies two integers"""
57
- return num1 * num2
58
 
59
- # MCP 应用挂载到主 FastAPI 应用的不同路径下
60
- app.mount("/app01", app01.app)
61
- app.mount("/app02", app02.app)
 
62
 
63
- # 根路径或其他API路由 (可选)
64
  @app.get("/")
65
  async def read_root():
66
- return {"message": "Multi-App MCP Server is running"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
- if __name__ == "__main__":
69
- # 运行主 FastAPI 应用,监听 7860 端口
70
- uvicorn.run(app, host="0.0.0.0", port=7860)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  ```
72
 
73
  **代码说明:**
74
- - 我们导入了必要的库:`uvicorn`, `FastAPI`, `FastMCP`, `mcp.types`。
75
  - 创建了主 `FastAPI` 应用 `app`。
76
  - 创建了两个 `FastMCP` 实例 `app01` 和 `app02`,分别代表两个独立的 MCP 应用。
77
- - 使用 `@app01.tool()` 和 `@app02.tool()` 装饰器为各自的应用添加了示例工具。
78
  - 最关键的是使用 `app.mount("/app_name", app_instance.app)` 将每个 `FastMCP` 实例的内部 FastAPI 应用挂载到主应用的 `/app_name` 路径下。
79
- - 最后,使用 `uvicorn.run()` 启动主应用,监听 7860 端口。
80
 
81
- ## 4. 项目进展与已完成工作
82
 
83
- 目前,项目的基础框架已经搭建完成,并成功实现了单端口多应用的架构。
84
-
85
- **已完成的工作:**
86
- - 成功搭建并运行了使用 MCP Python SDK 的单端口多应用 MCP 服务 (端口 7860)。
87
- - 为 `app01` 应用添加了 `app01_add_integers` 工具,并验证了其功能。
88
- - 为 `app02` 应用添加了 `app02_multiply_integers` 工具,并验证了其功能。
89
- - 成功处理了包含加法和乘法的复合数学表达式计算请求,验证了不同应用工具的协同工作。
90
-
91
- **当前状态:**
92
- 服务已基本框架搭建完成,并已成功添加并验证了两个应用的示例工具。
93
 
94
  ## 5. 在Cline中测试
95
 
96
  在Cline中,我们可以直接调用已连接的MCP服务器提供的工具进行测试。以下是一些测试算例:
97
 
 
 
98
  **测试1:1+1=?**
99
  使用 `app01` 的 `app01_add_integers` 工具计算:
100
  ```xml
@@ -172,7 +214,7 @@ if __name__ == "__main__":
172
  </use_mcp_tool>
173
  ```
174
  结果:4
175
- 然后计算 1+1+4
176
  ```xml
177
  <use_mcp_tool>
178
  <server_name>app01</server_name>
 
5
  在当前大模型智能体快速发展的背景下,如何有效地管理和调用各种外部工具(Tool)成为了一个关键问题。Model Context Protocol (MCP) 提供了一种标准化的方式来解决这个问题。本项目旨在利用 MCP 的 Python SDK,构建一个**单端口服务,能够加载并管理多个独立的 MCP 应用**。
6
 
7
  核心目标如下:
8
+ - **构建基于 Python SDK MCP 服务**__**: 项目使用 MCP Python SDK 构建MCP 服务。
9
+
10
+ - **实现单端口多应用架构**: 核心目标是创建一个可以在单个网络端口上托管多个独立 MCP 应用的服务。通过 FastAPI 和 FastMCP 实现这一架构,服务监听在 7860 端口。
11
+
12
+ - **支持按应用名称划分路径**: 实现通过不同的 URL 路径(例如 `/app01/sse`, `/app02/sse`)访问不同的 MCP 应用,符合 `http://localhost:7860/{app_name}/sse` 的路径格式要求。
13
+
14
+ - **集成并托管独立的 MCP 应用**: 创建 `App01` 和 `App02` 两个独立的 MCP 应用类,并将它们拆分到单独的文件中,然后在主 FastAPI 应用中挂载和托管这两个应用。
15
+
16
+ - **定义和暴露 MCP 工具**: 在每个应用中定义示例 MCP 工具(如 `app01_add_integers` 和 `app02_multiply_integers`),这些工具通过 MCP 协议暴露出来,可供兼容的客户端调用。
17
 
18
  ## 2. 技术选型与实现思路
19
 
 
30
  ## 3. 代码实现示例
31
 
32
  以下是 `app.py` 的核心代码示例,展示了如何使用 FastAPI 和 FastMCP 挂载多个应用:
33
+ **app.py**
34
  ```python
 
35
  from fastapi import FastAPI
 
 
36
 
37
+ from apps.app01 import App01
38
+ from apps.app02 import App02
 
 
 
 
39
 
40
+ # 初始化应用
41
+ app01_instance = App01()
42
+ app02_instance = App02()
 
 
 
 
 
 
 
 
43
 
44
+ # 创建 FastAPI 应用
45
+ app = FastAPI()
 
 
 
46
 
47
+ # 挂载 MCP 应用的 SSE 端点
48
+ # 使用 Mount 来指定路径前缀
49
+ app.mount("/app01", app=app01_instance.mcp.sse_app())
50
+ app.mount("/app02", app=app02_instance.mcp.sse_app())
51
 
52
+ # 根路径,用于测试 FastAPI 是否正常工作
53
  @app.get("/")
54
  async def read_root():
55
+ return {"message": "FastAPI is running"}
56
+ ```
57
+
58
+ # 创建第一个 MCP 应用实例 (app01)
59
+ **app01.py**
60
+ ```python
61
+ import asyncio
62
+ from fastapi import Request
63
+ from mcp.server.fastmcp import FastMCP
64
+
65
+ # 创建 MCP 应用 app01 的类
66
+ class App01:
67
+ def __init__(self):
68
+ self.mcp = FastMCP(name="app01", stateless_http=True)
69
+ self.register_endpoints()
70
+
71
+ def register_endpoints(self):
72
+ @self.mcp.resource("app01://info")
73
+ def app01_info() -> str:
74
+ """Information about App01"""
75
+ return "This is App01"
76
+
77
+ @self.mcp.tool()
78
+ def app01_echo(message: str) -> str:
79
+ """Echo a message in App01"""
80
+ return f"App01 Echo: {message}"
81
+
82
+ @self.mcp.tool()
83
+ def app01_add_integers(num1: int, num2: int) -> int:
84
+ """Add two integers in App01"""
85
+ return num1 + num2
86
+ ```
87
+
88
+ # 创建第二个 MCP 应用实例 (app02)
89
+ **app02.py**
90
+ ```python
91
+ import asyncio
92
+ from fastapi import Request
93
+ from mcp.server.fastmcp import FastMCP
94
 
95
+ # 创建 MCP 应用 app02 的类
96
+ class App02:
97
+ def __init__(self):
98
+ self.mcp = FastMCP(name="app02", stateless_http=True)
99
+ self.register_endpoints()
100
+
101
+ def register_endpoints(self):
102
+ @self.mcp.resource("app02://status")
103
+ def app02_status() -> str:
104
+ """Status of App02"""
105
+ return "App02 is running"
106
+
107
+ @self.mcp.tool()
108
+ def app02_process(data: str) -> str:
109
+ """Process data in App02"""
110
+ return f"App02 Processing: {data}"
111
+
112
+ @self.mcp.tool()
113
+ def app02_multiply_integers(num1: int, num2: int) -> int:
114
+ """Multiply two integers in App02"""
115
+ return num1 * num2
116
  ```
117
 
118
  **代码说明:**
119
+ - 我们导入了必要的库:`FastAPI`, `FastMCP`, `asyncio`, `Request`。
120
  - 创建了主 `FastAPI` 应用 `app`。
121
  - 创建了两个 `FastMCP` 实例 `app01` 和 `app02`,分别代表两个独立的 MCP 应用。
122
+ - 使用 `@self.mcp.resource()` 和 `@self.mcp.tool()` 装饰器为各自的应用添加了示例资源和工具。
123
  - 最关键的是使用 `app.mount("/app_name", app_instance.app)` 将每个 `FastMCP` 实例的内部 FastAPI 应用挂载到主应用的 `/app_name` 路径下。
 
124
 
125
+ ## 4. 实现的功能
126
 
127
+ 项目的基础框架搭建完成,并成功实现单端口多应用的架构。
128
+ - 搭建并运行使用 MCP Python SDK 的单端口多应用 MCP 服务 (端口 7860)。
129
+ - 将 `App01` 和 `App02` 类独立写入 `apps/app01.py` 和 `apps/app02.py` 文件中,再在 `app.py` 里导入这些类,简化主程序代码。
130
+ - 启动 FastAPI 服务器,在该服务器上托管 `app01` `app02` MCP 应用。
131
+ - 为 `app01` 应用添加 `app01_add_integers` 工具。
132
+ - 为 `app02` 应用添加 `app02_multiply_integers` 工具。
 
 
 
 
133
 
134
  ## 5. 在Cline中测试
135
 
136
  在Cline中,我们可以直接调用已连接的MCP服务器提供的工具进行测试。以下是一些测试算例:
137
 
138
+ **重要说明**: 当前的 MCP 服务使用了 Server-Sent Events (SSE) 作为传输协议。SSE 调用需要一个能够建立 SSE 连接并发送/接收 MCP 消息的客户端(如Cursor、Cline等)。此处使用 Cline 作为客户端进行测试。
139
+
140
  **测试1:1+1=?**
141
  使用 `app01` 的 `app01_add_integers` 工具计算:
142
  ```xml
 
214
  </use_mcp_tool>
215
  ```
216
  结果:4
217
+ 然后计算 1+1:
218
  ```xml
219
  <use_mcp_tool>
220
  <server_name>app01</server_name>