体育数据可视化服务

一个基于FastMCP和OpenAI Apps SDK的体育数据可视化服务,提供多联赛比赛日程和详细统计数据的交互式展示。

  • by jhpark-psynet
  • Python
  • 本地部署
  • 更新于
在 GitHub 查看 beanspapa/test-mcp-server

使用说明

测试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
  • ✅ 无需修改处理器即可添加新运动

运作原理

  1. React组件 → 在中构建为HTML/JS/CSS components/assets/
  2. Python服务器 从以下位置读取HTML文件 assets/
  3. MCP资源 将HTML作为小部件公开
  4. 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

发生了什么:

  1. 中的每个小部件 components/src/*/index.tsx 已编译
  2. JS/CSS/HTML文件是在 components/assets/
  3. 简单的文件名,如 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

许可证

麻省理工学院