MCP研究服务器
基于FastMCP的研究服务器,支持MCP(模型上下文协议),用于统一搜索、抓取和清理LLM就绪输出。可通过自动HTTPS的Tailscale VPN远程访问。
特性
- FastMCP服务器:用于Claude Desktop、Claude Code和其他MCP客户端的SSE传输
- Tailscale集成:通过MagicDNS自动HTTPS(例如。,
https://mcp-server.tailb1e597.ts.net) - 多页搜索:SearXNG(Brave、Bing、DuckDuckGo、Ask),10页分页
- 智能刮擦:Crawl4AI(快速)→ 硒基地(隐形后备)→ 黑名单
- PDF支持:使用PyMuPDF从PDF文件下载和提取文本
- 域速率限制:Redis支持并发请求限制
- 清洁输出:ContentCleaner,具有LLM就绪标记的优先级提取功能
- 领域学习:PostgreSQL跟踪每个域的工作方法
- 文档工具:本地FastMCP工具,用于将任何URL作为干净的Markdown获取
- Redis缓存:用于搜索、抓取和文档的响应处理中间件
- 错误处理:输入验证、ToolError异常、隐藏的内部错误
- VPN/代理网关:Gluetun容器路由全部通过WireGuard VPN隧道
- 代理管理:用于代理状态、测试和轮换的运行时工具
- Caddy反向代理:具有自动TLS的专业部署
建筑
┌─────────────────────────────────────────┐
│ Caddy (ports 80/443) │
│ Tailscale TLS via shared socket │
└──────────────┬──────────────────────────┘
│ Docker DNS (mcp_net)
┌──────────────▼──────────────────────────┐
│ mcp-server (SSE on :8000) │
│ ┌────────────────────────────────┐ │
│ │ Web Tools: │ │
│ │ • search_web │ │
│ │ • scrape_url │ │
│ │ • get_domains │ │
│ │ • clean_database │ │
│ └────────────────────────────────┘ │
│ ┌────────────────────────────────┐ │
│ │ Docs (native, namespace: docs_) │ │
│ │ • docs_list_sources │ │
│ │ • docs_fetch_docs │ │
│ └────────────────────────────────┘ │
└──────────────┬──────────────────────────┘
│
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ PostgreSQL │ │ Redis │ │ SearXNG │
│ (domains) │ │ (cache) │ │ (search) │
└──────────────┘ └──────────────┘ └──────────────┘
│
┌───────────────┼──────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Worker │ │ Beat │ │ Flower │
│(Celery) │ │(schedule)│ │(monitor) │
└──────────┘ └──────────┘ └──────────┘
External Traffic (scrape/search):
┌──────────┐ HTTP ┌──────────┐ WireGuard ┌───────────┐
│ MCP / │ ────────► │ Gluetun │ ──────────► │ Internet │
│ Celery │ :8888 │ (VPN) │ │ │
└──────────┘ └──────────┘ └───────────┘
Internal Traffic (postgres, redis, searxng):
┌──────────┐ direct ┌──────────────┐
│ MCP / │ ────────► │ Internal │
│ Celery │ │ services │
└──────────┘ └──────────────┘快速开始
1.先决条件
- Docker和Docker Compose
- Tailscale帐户(用于远程访问)
2.配置环境
# Copy example env file
cp .env.example .env
# Edit .env with your values:
# - TS_AUTHKEY: Get from https://login.tailscale.com/admin/settings/keys
# - TAILNET_DOMAIN: Your tailnet domain (e.g., tailb1e597.ts.net)
# - TAILNET_MACHINE_NAME: Your Tailscale machine name (e.g., mcp-server)3.启动服务
docker compose up -d4.通过Tailscale连接
Tailscale运行后,您的MCP服务器可以在以下位置访问:
https://./sse例子: https://gtek.tailb1e597.ts.net/sse
______________________________________________________________________
连接客户
克劳德代码(CLI)
claude mcp add --transport http research https://gtek.tailb1e597.ts.net/sse用您实际的Tailscale MagicDNS URL替换该URL。
克劳德桌面版
添加到您的Claude Desktop配置中:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json 窗户: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"research": {
"transport": "sse",
"url": "https://gtek.tailb1e597.ts.net/sse",
"timeout": 120000
}
}
}其他MCP客户端
任何兼容MCP的客户端都可以通过SSE传输连接到您的Tailscale HTTPS URL。
本地访问 (仅限同一台机器):
- MCP服务器:
http://localhost:8327/sse - 花卉仪表板:
http://localhost:5921 - VPN代理:
http://localhost:8328
______________________________________________________________________
可用工具
网络研究(无前缀)
| 工具 | 说明 |
|---|---|
search_web | 使用多个搜索引擎搜索网络 |
scrape_url | 删除URL并提取干净的标记 |
map_domain | 从站点地图中发现URL/通用爬网(URL发现) |
crawl_site | 使用BFS策略进行深度爬行(以下链接) |
scrape_structured | 使用预构建的模式提取结构化JSON数据 |
list_schemas | 列出可用的提取模式 |
get_domains | 使用首选方法列出跟踪的域 |
clean_database | 清除所有域跟踪数据 |
文档(前缀: docs_)
| 工具 | 说明 |
|---|---|
docs_list_sources | 列出可用的文档库 |
docs_fetch_docs | 从任何URL获取文档(缓存,清理为Markdown) |
代理管理
| 工具 | 说明 |
|---|---|
proxy_status | 显示当前代理配置和轮换统计信息 |
proxy_test | 测试代理连接并报告出口IP与真实IP |
proxy_rotate | 手动旋转到旋转列表中的下一个代理 |
______________________________________________________________________
结构化数据抽取
这 scrape_structured 该工具使用预构建的CSS模式从网页中提取结构化JSON——不需要LLM,更快、更便宜。
架构类型
| 架构 | 提取 |
|---|---|
ecommerce | 产品(名称、价格、评级、可用性、图片、网址、sku) |
news | 文章(标题、作者、日期、内容、类别、摘要) |
jobs | 列表(职位、公司、地点、工资、描述、类型) |
blog | 帖子(标题、作者、日期、内容、标签、摘录) |
social | 社交帖子(用户名、内容、时间戳、点赞、分享) |
products | 产品目录多项目提取 |
使用示例
# Extract products from an e-commerce page
scrape_structured(
url="https://shop.example.com/products",
schema_type="ecommerce"
)
# Returns: {"items": [{"title": "...", "price": "$99", "rating": "4.5", ...}]}
# Extract job listings
scrape_structured(
url="https://jobs.example.com",
schema_type="jobs"
)
# Returns: {"items": [{"title": "Engineer", "company": "...", "salary": "...", ...}]}
# Extract with custom selector
scrape_structured(
url="https://news.example.com",
schema_type="news",
custom_selector=".article-list article" # Override base selector
)何时使用scrape_structured与scrape_url
| 在以下情况下使用scrap_structured。.. | 在以下情况下使用scrap_url。.. |
|---|---|
| 您需要结构化的JSON数据 | 您需要完整的页面内容 |
| 提取特定字段(产品、作业) | 阅读文章、文档 |
| 构建数据集/分析 | 通用抓取 |
| 页面结构一致 | 页面结构未知 |
______________________________________________________________________
映射和爬网工作流
新的 map_domain 和 crawl_site 工具支持智能网站探索:
1.映射域(URL发现)
# Discover all blog posts from a site
map_domain(
domain="blog.example.com",
source="sitemap", # or "cc" for Common Crawl, "sitemap+cc" for both
pattern="*/posts/*", # Filter by URL pattern
max_urls=1000, # Maximum URLs to return
extract_head=True # Extract metadata (slower but richer)
)
# Relevance-based discovery
map_domain(
domain="docs.example.com",
source="sitemap",
query="API reference endpoints",
score_threshold=0.5, # Minimum BM25 relevance score
scoring_method="bm25"
)来源:
sitemap:快速XML站点地图解析(每秒100-1000个URLs)cc:通用爬网数据集(历史索引)sitemap+cc:两个来源均可实现最大覆盖
2.爬行站点(深度爬行)
# Crawl documentation with depth limit
crawl_site(
url="https://docs.example.com",
max_depth=2, # Follow links 2 levels deep
max_pages=50, # Maximum pages to crawl
pattern="*/api/*", # Filter by URL pattern
word_count_threshold=100 # Skip short pages
)战略:
- BFS(广度优先搜索)用于系统探索
- 尊重
max_depth(链接级别)和max_pages(总页数) - 可以按URL模式和字数进行过滤
- 返回每个已爬网页面的完整内容
典型工作流程
# Step 1: Discover URLs
result = map_domain(domain="python.langchain.com", pattern="*/docs/*")
# Step 2: Review discovered URLs
for url_info in result["urls"]:
print(url_info["url"], url_info.get("title", ""))
# Step 3: Deep crawl for full content
crawl_result = crawl_site(
url="https://python.langchain.com/docs/",
max_depth=2,
max_pages=20
)
# Step 4: Process crawled pages
for page in crawl_result["pages"]:
print(page["title"], len(page.get("content", "")))______________________________________________________________________
Docker服务
| 容器 | 用途 | 资源 |
|---|---|---|
| mcp-caddy | 带自动TLS的反向代理 | - |
| mcp服务器 | 带SSE传输的FastMCP服务器 | 512MB限制 |
| mcp-celery worker | Scrapeng worker(10个并行浏览器) | 3GB限制,2个CPU |
| mcp芹菜节拍 | 定期任务调度程序 | 256MB限制 |
| mcp-flower | 芹菜监控(本地主机:5555) | 256MB限制 |
| mcp-postgres | 域跟踪数据库 | 512MB限制 |
| mcp-redis | 缓存+速率限制 | 256MB限制 |
| mcp searxng | 多引擎搜索 | 512MB限制 |
| mcp vpn | Gluetun vpn网关(HTTP代理) | 256MB限制 |
| mcp-ts | 尾车(主机网络) | - |
______________________________________________________________________
VPN/代理设置
通过WireGuard VPN隧道通过 胶屯 集装箱。你的真实IP永远不会暴露在被抓取的网站上。
它是如何工作的:
- Gluetun通过WireGuard连接到VPN服务器
- 它在以下位置公开了一个HTTP代理
gluetun:8888在Docker网络上 - MCP服务器和Celery worker通过此代理路由所有外部HTTP流量
- 内部服务(Postgres、Redis、SearXNG)直接连接——它们被排除在代理之外
如果VPN隧道中断,Gluetun的防火墙会阻止所有出站流量。您的真实IP没有退路。
设置
1.从您的VPN提供商处获取WireGuard凭据
大多数提供商(Proton VPN、Mullvad、Surfshark、NordVPN等)都允许您下载WireGuard .conf 文件。它看起来像:
[Interface]
PrivateKey = ABC123abc...
Address = 10.2.0.2/32
DNS = 10.2.0.1
[Peer]
PublicKey = XYZ789xyz...
Endpoint = 1.2.3.4:51820
AllowedIPs = 0.0.0.0/02.将值添加到您的 .env
WIREGUARD_PRIVATE_KEY=ABC123abc... # from [Interface] PrivateKey
WIREGUARD_ADDRESS=10.2.0.2/32 # from [Interface] Address
WIREGUARD_PUBLIC_KEY=XYZ789xyz... # from [Peer] PublicKey
WIREGUARD_ENDPOINT_IP=1.2.3.4 # from [Peer] Endpoint IP
WIREGUARD_ENDPOINT_PORT=51820 # from [Peer] Endpoint port3.启动堆栈
docker compose up -d4.验证它是否正常工作
使用 proxy_test MCP工具——它报告您的代理退出IP和您的真实IP。如果 ip_different: true,您的流量正在通过VPN。
代理旋转
要在多个代理(例如,不同的VPN服务器或代理池)之间轮换,请设置 MCP_PROXY_URLS 相反:
# Comma-separated list — rotates round-robin by default
MCP_PROXY_URLS=http://gluetun:8888,http://proxy2:8080,socks5://proxy3:1080使用 proxy_rotate 手动前进到下一个代理,或 proxy_status 查看当前旋转状态。
高级配置
| 变量 | 默认值 | 描述 |
|---|---|---|
MCP_PROXY_URL | http://gluetun:8888 | 单代理URL(HTTP或SOCKS5) |
MCP_PROXY_URLS | _(空)_ | 逗号分隔的列表用于轮换(覆盖 MCP_PROXY_URL) |
MCP_PROXY_ROTATION | round-robin | 轮换策略: round-robin 或 random |
MCP_PROXY_EXCLUDE | searxng,postgres,redis,localhost,127.0.0.1 | 绕过代理的主机名 |
禁用VPN
要在没有代理的情况下运行,请注释掉 gluetun 服务中 docker-compose.yml 并设置 MCP_PROXY_URL= (空)在你的 .env所有交通都将直达。
______________________________________________________________________
环境变量
看 .env.example 对于所有可配置值:
# Tailscale
TS_AUTHKEY=tskey-auth-
TAILNET_DOMAIN=your-tailnet.ts.net
TAILNET_MACHINE_NAME=mcp-server
# PostgreSQL
POSTGRES_HOST=postgres
POSTGRES_DB=mcp_server
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
# SearXNG
SEARXNG_SECRET=
# Celery
CELERY_BROKER_URL=redis://redis:6379/0
CELERY_RESULT_BACKEND=redis://redis:6379/0
# API
MCP_PORT=8000
ALLOWED_ORIGINS=http://localhost:8000,http://127.0.0.1:8000
# VPN (WireGuard credentials from your VPN provider)
WIREGUARD_PRIVATE_KEY=
WIREGUARD_ADDRESS=10.2.0.2/32
WIREGUARD_PUBLIC_KEY=
WIREGUARD_ENDPOINT_IP=
WIREGUARD_ENDPOINT_PORT=51820
# Caching (seconds)
SEARCH_CACHE_TTL=300
SCRAPE_CACHE_TTL=3600
DOCS_CACHE_TTL=3600______________________________________________________________________
报废流程
1. Check Cache → Return cached if available
2. Check Rate Limit → Wait if too many concurrent to domain
3. Check Blacklist → Reject if blacklisted
4. PDF? → Extract text with PyMuPDF
5. Reddit? → Use Reddit JSON API
6. Check Database → Use learned preference
7. Try Crawl4AI (3x retry, fast, JS-enabled)
8. If failed → Try Selenium (3x retry, stealth mode)
9. If both failed → Blacklist domain
10. Clean HTML → Waterfall strategy for universal scraping内容提取优先级(瀑布策略):
- CSS选择器(如果提供)
- 瀑布 (Selectolax攻击性修剪+语义定位)
- 适用于所有页面类型:文章、SaaS、登录页、SPA - 积极删除垃圾标签(脚本、样式、导航、页脚、表单等) - 语义定位(`, , #content`) - 混乱布局的全身回退
- Trafilatura(仅用于新闻/博客的文章回退)
- BeautifulSoup(核选项)
______________________________________________________________________
错误处理
服务器实现了符合FastMCP的错误处理:
- 输入验证:使用所有参数
Annotated[Field]有限制条件
- min_length/max_length 对于字符串 - ge/le 用于数值范围 - Literal 用于枚举选择
- 用户面临错误:
ToolError用于客户端可见消息 - 内部错误:屏蔽客户端(安全),记录服务器端
- HTTP特定消息:404、403、500个错误返回有用的上下文
______________________________________________________________________
缓存
Redis支持的缓存,具有可配置的TTL:
# Cache TTL (seconds)
SEARCH_CACHE_TTL=300 # 5 minutes for search results
SCRAPE_CACHE_TTL=3600 # 1 hour for scraped content
DOCS_CACHE_TTL=3600 # 1 hour for documentation缓存的响应包括元数据(使用的方法、时间戳)和绕过昂贵的操作。
______________________________________________________________________
文档来源
服务器包括以下官方文档:
- LangGraph -代理框架
- LangChain -LLM框架(python.langchain.com,docs.langchain.com)
- 深度智能体 -LangChain代理模式
- 快速API -Web框架
- 派丹蒂克 -数据验证(docs.pydantic.dev,ai.pydanic.dev)
- FastMCP -MCP框架(gofastmcp.com)
- 码头工人 -集装箱平台
- Next.js -React框架
- Vercel 人工智能 -React的AI SDK(AI SDK.dev)
添加更多内容 docs_config.yaml。llms.txt文件中链接的域会被自动发现并允许。
______________________________________________________________________
技术栈
- FastMCP -带SSE传输的MCP服务器框架
- 卡迪 -具有自动TLS的反向代理
- PostgreSQL -领域跟踪和学习
- Celery+Redis -任务队列和速率限制
- 瑟克斯NG -多引擎搜索
- Crawl4AI -支持快速JS的抓取、URL种子和深度爬行
- 硒基 -隐形刮擦回退
- ContentCleaner -多策略HTML→Markdown转换
- 胶屯 -带WireGuard和HTTP代理的VPN网关
- 尾鳞 -VPN+DNS裁判
