内容
# 通过MCP将外部Web服务器连接到代理
## 本工作的目的
之前,我尝试了一个基本的本地MCP服务器,并测试了Smithery提供的远程MCP服务器。这次,我的目标是通过MCP直接将内部或外部Web服务器连接到代理。如果成功,这将使我能够将各种Web服务器重新利用为MCP服务器,以便更广泛地使用。
## 背景
关键任务是将与Webtoon相关的内部或外部API服务器转换为MCP服务器,并将其连接到代理。
MCP服务器文档仍然稀缺——尤其是在将通用HTTP Web服务器转换为MCP服务器的情况下。
经过一些研究,我发现了三种主要方法(大多数示例都是围绕FastAPI构建的):
- **FastMCP**(兼容FastAPI应用)→ 目前最受欢迎
- **FastAPI MCP**(为FastAPI服务器设计)
- **手动转换**(无库)
## 交付物
我首先将一个公共Webtoon API服务器转换为MCP服务器,而不是使用内部API。然后将其与LangChain和ChatGPT集成,以创建一个基本的*对话式Webtoon搜索代理*,可以处理诸如:
- “你能推荐一部Jo Seok的作品吗?”
- “找到Jo Seok最近的系列,并给我展示第3集的最佳评论。”
在这个过程中,我记录了我获得的见解和经验教训(包括失败的尝试),并得到了ChatGPT的帮助。
---
## 步骤
### [1] 选择和准备外部Webtoon API服务器
我选择了一个公共Webtoon API服务器:[https://github.com/HyeokjaeLee/korea-webtoon-api](https://github.com/HyeokjaeLee/korea-webtoon-api)
Swagger显示该服务器是用Node.js + Express构建的。
大多数MCP服务器库都是针对FastAPI的,因此我计划:
- 将Node.js API服务器转换为**FastAPI代理服务器**,然后
- 将其包装为MCP服务器。
```python
from fastapi import FastAPI, Request
import httpx
fastapi_app = FastAPI()
EXISTING_API_BASE = "https://korea-webtoon-api-cc7dda2f0d77.herokuapp.com"
@fastapi_app.get("/webtoons")
async def proxy_webtoons(request: Request):
async with httpx.AsyncClient() as client:
r = await client.get(f"{EXISTING_API_BASE}/webtoons", params=request.query_params)
return r.json()
@fastapi_app.get("/status")
async def proxy_status():
async with httpx.AsyncClient() as client:
r = await client.get(f"{EXISTING_API_BASE}/status")
return r.json()
```
**问题**:MCP无法解析来自此FastAPI代理的查询参数。
这使我探索可以一步将服务器转换为MCP兼容服务器的库。
---
### [2] 测试MCP服务器库
#### FastMCP
[https://github.com/jlowin/fastmcp](https://github.com/jlowin/fastmcp)
最受欢迎的选项,常用于LangChain示例中。
它提供了从FastAPI应用到MCP服务器的简单转换:
```python
from fastapi import FastAPI
from fastmcp import FastMCP
app = FastAPI()
@app.get("/status")
def get_status():
return {"status": "running"}
@app.post("/items")
def create_item(name: str, price: float):
return {"id": 1, "name": name, "price": price}
mcp_server = FastMCP.from_fastapi(app)
if __name__ == "__main__":
mcp_server.run()
```
**问题**:这仅在我将API编写为FastAPI应用时有效。当处理被代理的第三方Node.js API时,这并不理想。
在FastMCP中,GET方法被视为`resources`而不是`tools`,而文档/示例也有限。
LangChain的MCP适配器最近才添加了对`resources`的支持,文档仍然不足。
我尝试通过将GET方法视为POST工具来解决此问题,但查询参数仍未得到正确处理。
---
#### FastAPI MCP
[https://github.com/tadata-org/fastapi_mcp](https://github.com/tadata-org/fastapi_mcp)
在这篇Hugging Face博客中也有介绍:[https://huggingface.co/blog/lynn-mikami/fastapi-mcp-server](https://huggingface.co/blog/lynn-mikami/fastapi-mcp-server)
这个库专为FastAPI量身定制,适合我的代理设置。
**主要优点**:
- 可以一步将FastAPI代理转换为MCP服务器。
- 所有端点(即使是GET)都被视为MCP工具。
- 与LangChain客户端的平滑集成。
```python
from fastapi import FastAPI
from fastapi_mcp import FastApiMCP
import httpx
EXISTING_API_BASE = "https://korea-webtoon-api-cc7dda2f0d77.herokuapp.com"
app = FastAPI()
async def proxy_get_webtoons(keyword: str = None, genre: str = None):
params = {}
if keyword: params["keyword"] = keyword
if genre: params["genre"] = genre
async with httpx.AsyncClient() as client:
response = await client.get(f"{EXISTING_API_BASE}/webtoons", params=params)
return response.json()
app.add_api_route("/", proxy_get_webtoons, methods=["GET"], operation_id="get_webtoons")
mcp = FastApiMCP(app, name="My API MCP", description="MCP server for my API")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
```
---
### [3] LangChain + 客户端集成测试
我成功地将MCP服务器连接到使用SSE的LangChain客户端。
工具架构被自动解析,允许通过LLM调用工具:
```python
import asyncio
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_mcp_adapters.tools import load_mcp_tools
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4o")
async def main():
async with MultiServerMCPClient(
{
"webtoon": {
"url": "http://0.0.0.0:8000/mcp",
"transport": "sse",
}
}
) as client:
tools = client.get_tools()
agent = create_react_agent(model, tools)
response = await agent.ainvoke({"messages": "Find a recent comic by Jo Seok"})
print("Agent response:", response)
if __name__ == "__main__":
asyncio.run(main())
```
---
### [4] 主机服务模拟
我通过一个基本的Streamlit UI扩展了设置,以模拟主机服务平台。
我将本地Webtoon MCP服务器与远程MCP工具(如Playwright Automation)结合在一起。
**示例查询**:“找到Jo Seok的最新系列,并展示第3集的最佳评论。”
- **步骤1**:Webtoon MCP服务器检索Jo Seok的作品。
- **步骤2**:远程Playwright MCP自动化浏览器导航并收集评论。
- **步骤3**:ChatGPT提取并总结最佳评论。
---
## 结论
我成功地将外部Web API转换为MCP服务器,并将其连接到代理,以实现一个可行的概念验证。
尽管如此,基于MCP的架构仍在不断发展。许多组件需要互操作:
**代理(LLM)⇄ 框架(LangChain)⇄ MCP ⇄ MCP服务器库**
这些组件都在快速演变,这导致偶尔出现兼容性摩擦。
MCP不仅支持`tools`,还支持`resources`、`prompts`等。
不过,我仍然在想:
- 除了`tool`的格式,其他格式会获得广泛使用吗?
- 如果没有,未来版本会取消支持吗?
当工具响应包含大量数据(如整个网页)时,令牌效率变得至关重要。预处理和总结将是必不可少的。
展望未来,我认为最有价值的方向不仅在于技术探索,而在于识别**高潜力的现实商业场景**。