使用说明
测试MCP服务器
使用FastMCP和OpenAI Apps SDK支持React小部件的MCP服务器。
项目结构
test-mcp-server/
├── server/ # Python FastMCP server (Modularized!)
│ ├── main.py # Entry point (32 lines!)
│ ├── config.py # Configuration (Pydantic BaseSettings)
│ ├── errors.py # Error definitions
│ ├── logging_config.py # Logging setup
│ ├── models/ # Domain models
│ │ ├── widget.py # Widget dataclass
│ │ ├── tool.py # ToolDefinition (has_widget property)
│ │ └── schemas.py # Pydantic schemas
│ ├── services/ # Business logic
│ │ ├── asset_loader.py # HTML asset loading
│ │ ├── widget_registry.py # Widget registry
│ │ ├── tool_registry.py # Tool registry
│ │ ├── cache.py # TTL cache for API responses
│ │ └── sports/ # Modular Sports API (Phase 6)
│ │ ├── __init__.py # SportsClientFactory
│ │ ├── base/ # Base classes
│ │ │ ├── client.py # BaseSportsClient
│ │ │ ├── endpoints.py # Common endpoints
│ │ │ └── mapper.py # BaseResponseMapper
│ │ ├── basketball/ # Basketball module
│ │ ├── soccer/ # Soccer module
│ │ ├── volleyball/ # Volleyball module
│ │ └── football/ # Football module
│ ├── handlers/ # Tool handlers
│ │ └── sports.py # Sports data handlers
│ ├── factory/ # MCP server factory
│ │ ├── safe_wrapper.py # SafeFastMCPWrapper (Phase 2)
│ │ ├── server_factory.py # MCP server creation
│ │ └── metadata_builder.py # OpenAI metadata
│ ├── tests/ # Integration tests
│ │ ├── test_mcp.py # MCP integration tests
│ │ ├── test_sports_api_integration.py # Sports API tests
│ │ ├── test_sports_tools.py # Sports tools tests
│ │ ├── test_environment.py # Environment tests
│ │ └── unit/ # Unit tests
│ │ ├── test_endpoints.py
│ │ └── test_client_endpoints.py
│ └── requirements.txt
├── components/ # React UI components
│ ├── src/ # React source code
│ │ ├── example/ # Example widget (테스트용)
│ │ ├── game-result-viewer/ # Game results viewer
│ │ └── index.css # Shared styles
│ ├── assets/ # Built HTML/JS/CSS (generated)
│ ├── package.json
│ ├── tsconfig.json
│ ├── vite.config.ts
│ └── build.ts # Build script
├── tests/ # Additional tests
│ └── test_cache.py # Cache module tests (18 tests)
├── .env.development # Development environment
├── .env.production # Production environment
├── package.json # Root build scripts
├── API_INTEGRATION.md # API integration guide
├── ARCHITECTURE.md # Architecture documentation
├── CUSTOMIZATION_GUIDE.md # Customization guide
├── REFACTORING_PLAN.md # Refactoring plan (Phase 1-8 ✅)
└── README.md
最近的改进 (重构-2025年11月):
第一阶段 (模块化):
- ✅ 模块化
main.py: 933 → 32 线路(减少96.6%) - ✅ 分层架构:模型、服务、处理程序、工厂
- ✅ 17个组织良好的模块
第2阶段 (安全包装):
- ✅ 用于FastMCP内部API保护的SafeFastMCPWrapper
- ✅ FastMCP API变化的早期检测
- ✅ 清除调试错误消息
第三期 (Pydantic设置):
- ✅ 配置重构:数据类→ Pydantic基础设置
- ✅ 自动环境变量验证
- ✅ .支持自动加载的env文件
- ✅ 使用字段验证器进行类型安全
阶段4 (构建验证):
- ✅ 自动构建验证脚本
- ✅ HTML/JS/CSS存在性检查
- ✅ HTML引用验证
阶段5 (体育MCP实施):
- ✅ Sports API集成(get_games_by_sport,get_game_details)
- ✅ 带有球队/球员统计信息的游戏结果查看器小部件
- ✅ 多联盟支持(NBA、KBL、WKBL等)
- ✅ 清洁工具架构(2个生产工具)
第6阶段 (体育API模块化):
- ✅ 将体育API重构为模块化结构
- ✅ 工厂模式(SportsClientFactory)
- ✅ 基类(BaseSportsClient、BaseResponseMapper)
- ✅ 特定运动模块(篮球、足球、排球、足球)
- ✅ 提高了可读性和可扩展性
第7阶段 (TTL缓存实现):
- ✅ 游戏列表API响应的内存内TTL缓存
- ✅ 可配置TTL(默认:5分钟)和最大大小(默认:100)
- ✅ 缓存前的数据验证(必填字段检查)
- ✅
force_refresh缓存旁路参数 - ✅ 缓存命中/未命中日志以进行调试
- ✅ 缓存模块的18个单元测试
第8阶段 (多运动处理器重构):
- ✅ 重构
get_game_details_handler所有体育支持 - ✅ 扩展的
BaseSportsClient使用可选方法(get_lineup,get_team_rank,get_team_vs_list) - ✅ 扩展的
BaseResponseMapper随着build_game_records()抽象方法 - ✅ 体育专项
gameRecords实施(篮球、足球、排球、足球) - ✅ 通过以下方式进行动态特征检测
has_operation()检查 - ✅ 处理程序代码缩减:713→ 624 lines
- ✅ 无需修改处理器即可添加新运动
运作原理
- React组件 → 在中构建为HTML/JS/CSS
components/assets/ - Python服务器 从以下位置读取HTML文件
assets/ - MCP资源 将HTML作为小部件公开
- ChatGPT客户端 使用来自的道具渲染小部件
structuredContent
React (TSX) → Build → HTML → MCP Server → ChatGPT (Render)
↓
structuredContent (props)
可用小部件
该服务器包括用于体育数据可视化的小部件:
1.小部件示例(example)-仅测试
- 目的:Demonstrates basic widget functional(用于测试)
- 属性:
message(字符串) - 位置:
components/src/example/ - 状态:仅保留用于测试的代码(不包括在工具列表中)
2.游戏结果查看器(game-result-viewer)
- 目的:显示详细的比赛统计数据,包括球队和球员统计数据
- 属性:
GameData(联赛、日期、状态、主队、客场、比赛记录) - 位置:
components/src/game-result-viewer/ - 特性:
- 带有球队得分和联赛信息的比赛标题
- 团队统计对比表
- 球员统计表(得分、篮板、助攻、投篮百分比)
- Zod模式验证
- 采用Tailwind CSS的响应式设计
- 由...使用:
get_game_details工具
可用工具
服务器提供2个运动数据MCP工具:
1.按运动获取游戏(文本工具)
- 名字:
get_games_by_sport - 类型:基于文本的工具
- 输入:
date(字符串)-YYYYMMDD格式的日期(例如,“20251118”)sport(string)-运动类型:篮球、足球、排球或足球force_refresh(布尔型,可选)-从API强制刷新,忽略缓存
- 输出:带有游戏时间表和结果的格式化文本
- 特性:
- 按联盟列出比赛(NBA、KBL、WKBL等)
- 显示分数、时间、竞技场和游戏状态
- 包括用于详细查询的游戏ID
- 团队别名支持(例如,“勇士队”->“金州勇士队”)
- TTL缓存:缓存结果5分钟以减少API调用
- 示例:退货:
{"date": "20251118", "sport": "basketball"}## Basketball Games on 20251118 ### [NBA] - **클리블랜드** 118 - 106 **밀워키** (Finished, W) - Arena: 로킷 모기지 필드하우스 - Game ID: `OT2025313104229`
2.获取游戏详细信息(小工具)
- 名字:
get_game_details - 类型:基于小部件的工具
- 输入:
game_id(string)-get_games_by_sport结果中的游戏ID
- 输出:带有详细游戏统计信息的交互式小部件
- 小部件:使用游戏结果查看器组件
- 特性:
- 带有最终得分的比赛头球
- 球队统计数据(投篮命中率、篮板、助攻、失误等)
- 球员统计数据(得分、篮板、助攻、投篮命中率等)
- 响应式设计的可排序表格
- 支持所有游戏状态:
- 赛前(状态='s'):团队比较、正面交锋记录、排名
- 比赛期间(状态='p'):实时比分、当前统计数据
- 赛后(状态='f'):最终得分、完整球员统计数据、比赛记录
- 示例:
{"game_id": "OT2025313104237"}
设置
1.安装依赖项
# Install all dependencies (Python + Node)
npm run install:all
# Or install separately
npm run install:components # Install React dependencies
npm run install:server # Install Python dependencies
2.构建React组件
npm run build
这将:
- 构建React组件
components/src/*/index.tsx - 在中生成HTML/JS/CSS
components/assets/
3.环境配置
服务器支持具有单独配置的多个环境(开发、生产)。
开发环境(默认)
# The .env.development file is used automatically
npm run server # or npm run server:dev
设置:
- 用途 模拟数据 (没有真正的API调用)
- 已启用调试日志记录
- 端口8000(本地主机)
生产环境
# Create/edit .env.production file
vi .env.production
# Set required values:
# - SPORTS_API_KEY=your_actual_api_key
# - USE_MOCK_SPORTS_DATA=false
# - LOG_LEVEL=INFO
# Run in production mode
ENV=production npm run server:prod
# or
npm run start:prod
设置:
- 用途 真实API 电话
- 信息记录
- 生产就绪配置
环境文件
.env.development-开发设置(自动加载,忽略).env.production-生产设置(手动创建,gitignored)
重要:从不承诺 .env.development 或 .env.production 去git!
4.运行服务器
# Development (default)
npm run server
# Development (explicit)
npm run server:dev
# Production
npm run server:prod
MCP服务器将启动 http://0.0.0.0:8000
构建过程
npm run build
发生了什么:
- 中的每个小部件
components/src/*/index.tsx已编译 - JS/CSS/HTML文件是在
components/assets/ - 简单的文件名,如
example.js,example.css
生成的文件:
components/assets/
├── example.js
├── example.css
├── example.html
├── game-result-viewer.js
├── game-result-viewer.css
├── game-result-viewer.html
└── manifest.json
构建验证
构建过程包括自动验证,以便及早发现问题:
# Build and verify (recommended)
npm run build
# Build without verification
npm run build:only
# Verify existing build
npm run build:verify
已验证的内容:
- ✅ 所有小部件都有HTML文件
- ✅ 所有小部件都有JS文件
- ✅ HTML引用指向现有文件
- ✅ 没有损坏的资产引用
输出示例:
Verifying widget builds...
============================================================
Widget: example
HTML: ✅ example.html
JS: ✅ example.js
CSS: ✅ example.css
Widget: game-result-viewer
HTML: ✅ game-result-viewer.html
JS: ✅ game-result-viewer.js
CSS: ✅ game-result-viewer.css
============================================================
✅ All widget builds verified successfully!
Verified 2 widget(s):
- example
- game-result-viewer
如果验证失败,构建过程将退出并显示错误,并显示缺少的内容。请修复构建,然后重试。
开发工作流程
快速开始
npm run install:all # First time only
npm run dev # Build + Start server
观看模式
# Terminal 1: Watch and rebuild on changes
npm run build:watch
# Terminal 2: Run server with auto-reload
npm run server
创建新小部件
1.创建React组件
mkdir -p components/src/my-widget
创建 components/src/my-widget/index.tsx:
import { createRoot } from 'react-dom/client';
interface MyWidgetProps {
data?: string;
}
function MyWidget({ data = "default" }: MyWidgetProps) {
return (
<div>
<h1>My Widget</h1>
<p>{data}</p>
</div>
);
}
const rootElement = document.getElementById('my-widget-root');
if (rootElement) {
const root = createRoot(rootElement);
root.render(<MyWidget />);
}
export default MyWidget;
2.建造
npm run build
这会产生 components/assets/my-widget.html
3.在服务器中注册
编辑 server/main.py:
widgets: List[Widget] = [
Widget(
identifier="my-widget",
title="My Widget",
template_uri="ui://widget/my-widget.html",
invoking="Loading my widget",
invoked="My widget loaded",
html=_load_widget_html("my-widget"),
response_text="Rendered my widget!",
),
]
4.重新启动服务器
npm run server
将Props传递给React组件
道具通过以下方式传递 structuredContent 在工具响应中:
return types.ServerResult(
types.CallToolResult(
content=[...],
structuredContent={"data": "Hello from MCP!"}, # ← Props
_meta={
"openai.com/widget": widget_resource.model_dump(mode="json"),
}
)
)
React组件从ChatGPT的渲染引擎接收这些作为道具。
静态资产服务器
独立服务资产(用于测试):
cd components
npm run serve
资产将在 http://localhost:4444
环境变量
BASE_URL
为生成的HTML资源引用设置基本URL:
BASE_URL=http://your-domain.com:4444 npm run build
违约: http://localhost:4444
外部API配置
配置外部API集成:
EXTERNAL_API_BASE_URL=https://api.example.com
EXTERNAL_API_KEY=your-api-key-here
EXTERNAL_API_TIMEOUT_S=10.0 # Optional, default: 10.0
EXTERNAL_API_AUTH_HEADER=Authorization # Optional, default: Authorization
EXTERNAL_API_AUTH_SCHEME=Bearer # Optional, default: Bearer
看 外部API集成 了解更多详情。
体育API集成
服务器与体育数据API集成,以提供比赛时间表和详细统计数据:
特性
- 多联盟支持:NBA、KBL、WKBL等
- 游戏时间表:按日期和运动列出游戏
- 详细统计:带有交互式小部件的球队和球员统计数据
- 实时数据:获取当前游戏结果和分数
- 团队别名:支持常见的团队名称变体
模块化架构(第6阶段)
API运动版采用工厂模式,针对特定运动型客户:
SportsClientFactory.create_client("basketball") # Returns BasketballClient
SportsClientFactory.create_client("soccer") # Returns SoccerClient
SportsClientFactory.create_client("volleyball") # Returns VolleyballClient
SportsClientFactory.create_client("football") # Returns FootballClient
模块结构 (server/services/sports/):
__init__.py-SportsClient工厂base/client.py-BaseSportsClient(抽象基类)base/mapper.py-BaseResponseMapper(字段映射)basketball/-篮球专项实施soccer/-足球专项实施volleyball/-排球专项实施football/-足球专项实施
特性:
- 用于开发和测试的模拟数据
- 未配置API时自动回退到模拟
- 游戏、团队和玩家的结构化数据模型
- 支持多个联赛和运动
- 新运动的可扩展架构
- 特定运动的比赛记录
build_game_records()
特定运动比赛记录:
| 体育 | 统计 |
|---|---|
| 篮球 | 场上进球,三分球,罚球,篮板球,助攻,转身,抢断,拦网,犯规(9) |
| Soccer | 射门,有效射门,占有率,传球,传球成功率,犯规,角球,越位(8) |
| Volleyball | 进攻成功率,拦网,发球得分,发球失败,接发球效率,每局得分,迪格(7) |
| Football | 总码、通行码、跑动码、1st倒地、终结者、拥有时间、3rd倒地成功率(7) |
用法
按运动获取游戏
# Via MCP tool call
{
"name": "get_games_by_sport",
"arguments": {
"date": "20251118",
"sport": "basketball"
}
}
返回带有游戏列表、分数和游戏ID的格式化文本。
获取游戏详细信息
# Via MCP tool call
{
"name": "get_game_details",
"arguments": {
"game_id": "OT2025313104237"
}
}
返回一个交互式小部件,其中包含:
- 带有球队名称和得分的比赛标题
- 团队统计表
- 带有详细指标的玩家统计表
- 针对不同屏幕尺寸的响应式设计
测试
运行运动工具测试:
.venv/bin/python test_sports_tools.py
测试套件包括:
- get_games_by_sport处理器测试
- get_game_details处理程序测试
- 数据结构验证
故障排除
“未找到小部件HTML”
确保你已经构建了React组件:
npm run build
未安装Python依赖项
source .venv/bin/activate # Activate virtual environment
npm run install:server # Install dependencies
端口8000已在使用中
编辑 server/main.py 并更改端口:
uvicorn.run("main:app", host="0.0.0.0", port=8001)
测试
该项目包括全面的测试覆盖范围:
单元测试
单独测试单个模块:
# Activate virtual environment
source .venv/bin/activate
# Run cache module tests
pytest tests/test_cache.py -v
# Run endpoint unit tests
pytest server/tests/unit/ -v
测试覆盖率:
tests/test_cache.py:TTL缓存功能(18次测试)server/tests/unit/test_endpoints.py:端点配置server/tests/unit/test_client_endpoints.py:客户端端点
结果:所有单元测试通过
集成测试
测试完整的MCP服务器:
# Run MCP server integration tests
.venv/bin/python server/tests/test_mcp.py
# Run sports tools tests
.venv/bin/python server/tests/test_sports_tools.py
# Run sports API integration tests
.venv/bin/python server/tests/test_sports_api_integration.py
测试覆盖率 (server/tests/test_mcp.py):
- ✅ 小部件加载
- ✅ 工具加载(2个运动工具)
- ✅ MCP协议工具列表
- ✅ MCP协议资源列表
- ✅ 小部件工具执行(get_game_details)
- ✅ 文本工具执行(get_game_by_sport)
- ✅ 资源读取(小部件HTML)
结果:通过运动功能测试
运动工具测试 (server/tests/test_sports_tools.py):
- ✅ get_games_by_sport处理程序
- ✅ get_game_details处理程序
- ✅ 数据验证
结果:所有测试均通过
技术栈
后端
- FastMCP 2.0:MCP服务器框架
- httpx:外部API调用的异步HTTP客户端
- Uvicorn:ASGI web服务器
- 斯塔雷特:Web框架
- pytest:支持异步的测试框架
前端
- 反应19:UI组件库
- TypeScript:类型安全的JavaScript
- 顺风CSS 4.1:实用程序优先的CSS框架
- 黄道带:运行时类型验证
构建工具
- 快7:快速构建工具和开发服务器
- esbuild:JavaScript打包器
- npm:包管理器
协议
- MCP(模型上下文协议):LLM客户端和服务器之间的通信协议
文档
入门指南
- 定制_ GIDE.md -应用于实际项目 必读!
- 添加新部件(Weather Widget示例)
- 添加新工具(简单/复杂工具)
- 集成外部API
- 准备测试和部署
- 删除示例项目
建筑与设计模式
- 建筑.md -关键设计模式和体系结构决策
- SafeFastMCPWrapper模式:FastMCP内部API安全包装
- 外部API工具管理:动态工具注册结构
- 分层体系结构:分离关注点
规划与进展
技术文档
部署
Cloudflare 隧道
服务器可以使用Cloudflare隧道暴露在互联网上:
# Install cloudflared
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb -o /tmp/cloudflared.deb
sudo dpkg -i /tmp/cloudflared.deb
# Login and create tunnel
cloudflared tunnel login
cloudflared tunnel create mcpapps
# Configure tunnel (/etc/cloudflared/config.yml)
tunnel: <UUID>
credentials-file: /etc/cloudflared/<UUID>.json
ingress:
- hostname: mcpapps.yourdomain.com
service: http://localhost:8000
- service: http_status:404
# Route DNS and start
cloudflared tunnel route dns mcpapps mcpapps.yourdomain.com
sudo cloudflared service install
sudo systemctl start cloudflared
生产URL: https://mcpapps.selfwell.kr/mcp
许可证
麻省理工学院