mcp_calc

zkangHUST/mcp_calc

3.1

If you are the rightful owner of mcp_calc and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.

This project is a demonstration of a ChatGPT Widget using the OpenAI Apps SDK and Model Context Protocol (MCP) to create a custom UI component for displaying calculation results.

Tools
1
Resources
0
Prompts
0

ChatGPT Widget Demo - 计算器应用

📋 项目概述

这是一个基于 OpenAI Apps SDKModel Context Protocol (MCP) 开发的 ChatGPT Widget 示例项目。该项目演示了如何在 ChatGPT 中创建自定义 UI 组件,将工具的计算结果以美观的界面形式展示给用户。

核心特性

  • MCP 服务器:使用 FastMCP 框架构建 Python 后端服务
  • 自定义 Widget:在 ChatGPT 对话中嵌入交互式 UI 组件
  • 结构化数据:通过 structuredContent 传递计算结果
  • 现代化 UI:渐变背景、卡片设计、动画效果
  • 响应式设计:支持深色模式,适配不同设备

🏗️ 技术架构

技术栈

  • 后端:Python 3.x + FastMCP
  • 前端:HTML + CSS + Vanilla JavaScript
  • 协议:Model Context Protocol (MCP)
  • 框架:OpenAI Apps SDK

架构图

┌─────────────────┐
│   ChatGPT UI    │
│  (用户界面)      │
└────────┬────────┘
         │
         │ HTTP/SSE
         │
┌────────▼────────┐
│   MCP Server    │
│  (main.py)      │
│                 │
│  - 工具定义      │
│  - 资源管理      │
│  - 计算逻辑      │
└────────┬────────┘
         │
         │ 返回 HTML + structuredContent
         │
┌────────▼────────┐
│  Widget HTML    │
│ (calculatorv4)  │
│                 │
│  - 读取数据      │
│  - 渲染界面      │
│  - 事件监听      │
└─────────────────┘

🔑 核心概念

1. MCP Server

MCP Server 是提供工具和资源的后端服务。在本项目中:

  • 工具 (Tool)calculator - 执行数学计算
  • 资源 (Resource)calculatorv4.html - Widget 的 HTML 模板
  • 资源模板 (Resource Template):定义 Widget 的 URI 模式

2. Widget

Widget 是在 ChatGPT 对话中嵌入的自定义 UI 组件:

  • 运行在 iframe 中,与 ChatGPT 隔离
  • 通过 window.openai API 与宿主通信
  • 接收 structuredContent 数据进行渲染

3. 数据流

用户输入 → ChatGPT → MCP Server → 计算 → 返回结果
                                              ↓
                                    structuredContent
                                              ↓
                                    Widget HTML 渲染

📁 项目结构

mcp-test3/
├── server/
│   └── main.py              # MCP 服务器主文件
├── assets/
│   ├── calculator.html      # Widget v1 (基础版本)
│   ├── calculatorv2.html    # Widget v2 (改进版本)
│   ├── calculatorv3.html    # Widget v3 (事件监听版本)
│   └── calculatorv4.html    # Widget v4 (当前版本,带美观样式)
└── README.md                # 本文档

🚀 快速开始

1. 安装依赖

pip install fastmcp pydantic

2. 启动服务器

cd server
python main.py

服务器将在 http://0.0.0.0:8000 启动。

3. 配置 ChatGPT

在 ChatGPT 中配置 MCP 服务器连接(具体配置方式取决于你的 ChatGPT 客户端)。

4. 测试计算器

在 ChatGPT 中发送消息:

请帮我计算 123 + 456

ChatGPT 会调用 calculator 工具,并在对话中显示美观的计算结果 Widget。

💻 代码详解

服务器端 (main.py)

1. Widget 定义
calculator_widget = CalculatorWidget(
    identifier="calculator",
    title="Simple Calculator",
    template_uri="ui://widget/calculatorv4.html",  # Widget URI
    invoking="Calculating...",                      # 调用中提示
    invoked="Calculation finished",                 # 完成提示
    html=_load_widget_html("calculatorv4"),        # HTML 内容
    response_text="Calculator result rendered!",
)
2. 工具定义
@mcp._mcp_server.list_tools()
async def _list_tools() -> List[types.Tool]:
    return [
        types.Tool(
            name="calculator",
            title="Simple Calculator",
            description="A simple calculator...",
            inputSchema=TOOL_INPUT_SCHEMA,
            _meta=_tool_meta(widget),  # 关键:告诉客户端这是 Widget
            annotations={
                "destructiveHint": False,
                "openWorldHint": False,
                "readOnlyHint": True,
            },
        )
    ]

关键 Meta 字段

  • openai/outputTemplate: Widget 的模板 URI
  • openai/widgetAccessible: 允许 Widget 访问
  • openai/resultCanProduceWidget: 结果可以生成 Widget
3. 资源暴露
@mcp._mcp_server.list_resources()
async def _list_resources() -> List[types.Resource]:
    return [
        types.Resource(
            name=widget.title,
            uri=widget.template_uri,
            mimeType="text/html+skybridge",  # 关键:Skybridge MIME 类型
            _meta=_tool_meta(widget),
        )
    ]
4. 工具调用处理
async def _call_tool_request(req: types.CallToolRequest):
    # ... 计算逻辑 ...
    
    # 返回结构化数据
    structured = {
        "operand1": op1,
        "operand2": op2,
        "operator": op,
        "result": result,
    }
    
    return types.ServerResult(
        types.CallToolResult(
            content=[types.TextContent(...)],
            structuredContent=structured,  # 关键:结构化数据
            _meta=meta,
        )
    )

客户端 (calculatorv4.html)

1. 数据获取
function renderFromGlobals() {
    const globals = window.openai;
    const toolOutput = globals.toolOutput;
    
    // 获取结构化数据
    let data = toolOutput;
    if (toolOutput.structuredContent) {
        data = toolOutput.structuredContent;
    }
    
    // 渲染界面
    if (data && data.result !== undefined) {
        // 显示计算结果
    }
}
2. 事件监听

根据 OpenAI Apps SDK 文档,应该监听 openai:set_globals 事件:

window.addEventListener("openai:set_globals", (event) => {
    renderFromGlobals();
});
3. window.openai API

Widget 可以通过 window.openai 访问:

  • window.openai.toolOutput - 工具输出数据
  • window.openai.toolInput - 工具输入数据
  • window.openai.locale - 本地化设置
  • window.openai.theme - 主题(light/dark)
  • window.openai.callTool() - 调用其他工具
  • window.openai.sendFollowUpMessage() - 发送后续消息

🎨 UI 设计亮点

calculatorv4.html 特性

  1. 渐变背景:紫色渐变,现代感强
  2. 卡片设计:白色卡片,圆角阴影
  3. 分层显示:表达式和结果分开,结果更突出
  4. 动画效果
    • 滑入动画
    • 加载旋转图标
    • 悬停效果
  5. 深色模式:自动适配系统偏好
  6. 响应式布局:适配不同屏幕尺寸

📊 Demo 演示步骤

1. 启动服务器

cd server
python main.py

2. 在 ChatGPT 中测试

场景 1:简单加法

用户:帮我算一下 25 + 17

场景 2:乘法运算

用户:计算 123.5 * 4.2

场景 3:除法运算

用户:100 除以 3 等于多少?

场景 4:错误处理

用户:10 除以 0 是多少?

3. 观察效果

  • ✅ ChatGPT 调用 calculator 工具
  • ✅ 显示 "Calculating..." 提示
  • ✅ Widget 加载并显示计算结果
  • ✅ 美观的 UI 界面展示

🔍 关键实现细节

1. MIME 类型

Widget 必须使用 text/html+skybridge MIME 类型,这是 OpenAI 的 Skybridge 运行时标识。

2. Meta 字段

Meta 字段告诉 ChatGPT 客户端如何处理工具响应:

{
    "openai/outputTemplate": "ui://widget/calculatorv4.html",
    "openai/widgetAccessible": True,
    "openai/resultCanProduceWidget": True,
}

3. structuredContent

structuredContent 是传递给 Widget 的数据,必须是 JSON 可序列化的对象。

4. 事件系统

  • openai:set_globals - 当全局变量更新时触发
  • Widget 应该监听此事件以响应数据变化

5. 异步初始化

由于 window.openai 可能异步加载,Widget 需要:

  • 初始检查
  • 延迟检查(100ms, 500ms, 1000ms)
  • 轮询机制(作为后备方案)

🐛 常见问题

Q: Widget 显示 "Waiting for calculation…"

A: 检查以下几点:

  1. window.openai 是否已初始化
  2. toolOutput 是否存在
  3. 事件监听器是否正确设置
  4. 打开浏览器控制台查看调试信息

Q: 数据格式不匹配

A: 确保服务器返回的 structuredContent 结构与 Widget 期望的一致:

structured = {
    "operand1": float,
    "operand2": float,
    "operator": str,
    "result": float,
}

Q: Widget 不显示

A: 检查:

  1. Meta 字段是否正确设置
  2. MIME 类型是否为 text/html+skybridge
  3. Resource 是否正确注册
  4. template_uri 是否匹配

📚 参考资料

🎯 下一步计划

  • 添加更多计算功能(科学计算、单位转换等)
  • 支持历史记录显示
  • 添加交互式按钮(重新计算、复制结果等)
  • 支持多语言界面
  • 添加更多 Widget 示例

👥 团队协作

开发流程

  1. 后端开发:修改 server/main.py,添加新工具或功能
  2. 前端开发:修改 assets/calculatorv4.html,优化 UI
  3. 测试:在 ChatGPT 中测试完整流程
  4. 版本管理:创建新的 HTML 版本(如 calculatorv5.html)

代码规范

  • Python:遵循 PEP 8
  • JavaScript:使用现代 ES6+ 语法
  • HTML/CSS:使用语义化标签,响应式设计

项目维护者:[你的名字]
最后更新:2024年