用 TypeScript 实现石头剪刀布 MCP App:从工具注册到前端组件实战(mcp-for-beginners 第 15 章作业解析)
教程文档人工智能【免费下载链接】mcp-for-beginnersThis open-source curriculum introduces the fundamentals of Model Context Protocol (MCP) through real-world, cross-language examples in .NET, Java, TypeScript, JavaScript, Rust and Python. Designed for developers, it focuses on practical techniques for building modular, scalable, and secure AI workflows from session setup to service orchestration.项目地址https://gitcode.com/GitHub_Trending/mc/mcp-for-beginners点击查看免费下载本篇技术指南围绕 mcp-for-beginners 开源课程第 15 章MCP Apps的 TypeScript 作业展开完整讲解「石头剪刀布Rock Paper Scissors」解决方案的三层代码结构server.ts中的工具与组件资源注册、mcp-app.ts中的事件绑定与工具调用、mcp-app.html中的交互式 UI。读完本文你将掌握如何用modelcontextprotocol/ext-apps与modelcontextprotocol/sdk搭建一个同时返回数据与可渲染 UI 的 MCP App并能在 Visual Studio Code 或独立 Host 中启动、联调与验证它。MCP Apps让工具返回「可交互的组件」而不是裸数据在进入作业代码之前需要先理解作业背后的范式。正如 15-mcp-apps 课程主文档 所阐述的MCP Apps 是 MCP 标准中的新范式它不再要求工具调用只返回数据而是允许服务器同时声明「这份数据应该如何被用户交互」。也就是说工具结果可以携带 UI 信息——一个自包含的组件从数据到用户界面一应俱全避免了开发者自行编写和长期维护前置前端页面的开销。实现一个 MCP App 需要两部分彼此关联的注册工具Tool负责接收参数、执行逻辑并返回数据组件资源App Resource负责提供打包后的 HTML/JavaScript即可渲染的 UI。两者通过同一个resourceUri连接起来。整个 MCP Apps 的运行模型可以概括为Host 应用把 MCP App 的 UI 注入到 IFrame 容器中IFrame 内的事件处理器通过向父页面发送消息来调用服务器工具再把工具结果数据渲染回界面详见主文档中的 Mermaid 流程图。本作业正是对上述范式的直接练习作业要求实现一个石头剪刀布游戏UI 部分需要一个下拉列表、一个提交按钮和一个显示「谁出了什么、谁赢了」的标签服务端部分需要一个以choice为输入、随机生成电脑选择并判定胜负的工具。对应的解决方案文档即 assignment/typescript/README.md。解决方案的整体结构作业的官方解答刻意只保留了「真正关键」的代码UI 标记markup、事件绑定event wire up和服务端功能server features完整的构建配置、依赖声明与启动入口则在课程code目录中提供。my-app server.ts -- 服务端功能工具 组件资源注册 src mcp-app.ts -- UI 与事件绑定逻辑 mcp-app.html -- UI 标记对照仓库中的实际文件作业解答存放于 assignment/typescript/my-app文件职责仓库路径server.ts注册play-rps工具与ui://get-time/mcp-app.html资源并读取打包后的 HTMLserver.tssrc/mcp-app.ts创建App实例、绑定下拉框与按钮事件、通过callServerTool调用后端工具mcp-app.tsmcp-app.html定义石头剪刀布的下拉列表、提交按钮与结果展示区mcp-app.html服务端registerAppTool注册游戏工具服务端核心是 assignment 版 server.ts。它先创建一个McpServer实例然后用modelcontextprotocol/ext-apps/server提供的registerAppTool注册名为play-rps的工具import { registerAppResource, registerAppTool, RESOURCE_MIME_TYPE, } from modelcontextprotocol/ext-apps/server; import { McpServer } from modelcontextprotocol/sdk/server/mcp.js; import zod from zod; const resourceUri ui://get-time/mcp-app.html; registerAppTool( server, play-rps, { title: Play Rock-Paper-Scissors, description: Play a game of rock-paper-scissors with the server., inputSchema: zod.object({ choice: zod.enum([rock, paper, scissors]), }), _meta: { ui: { resourceUri } }, // 将工具与其 UI 资源关联 }, async ({ choice }) { const options [rock, paper, scissors] as const; const serverChoice options[Math.floor(Math.random() * options.length)]; let result: string; if (choice serverChoice) { result Its a tie! We both chose ${choice}.; } else if ( (choice rock serverChoice scissors) || (choice paper serverChoice rock) || (choice scissors serverChoice paper) ) { result You win! You chose ${choice} and I chose ${serverChoice}.; } else { result I win! You chose ${choice} and I chose ${serverChoice}.; } return { content: [{ type: text, text: result }], }; }, );值得注意的三个设计点输入校验inputSchema使用 zod 的zod.enum([rock, paper, scissors])限定choice只能取三种合法值从协议层杜绝非法输入。这与课程主文档中 FAQ 示例的zod.string().default(shipping)一脉相承——需要接受用户输入的工具都应声明inputSchema。UI 关联元数据_meta: { ui: { resourceUri } }是 MCP Apps 的关键连线——Host 调用该工具时会读取_meta.ui.resourceUri得知应该获取并渲染哪个资源作为交互式 UI。游戏判定逻辑服务端随机生成电脑选择按「平局—玩家赢—服务器赢」的顺序判定并把结果以标准 MCPcontent: [{ type: text, text: result }]结构返回。服务端registerAppResource注册组件资源工具只负责「算」UI 则来自组件资源。同一文件的第 64-86 行 用registerAppResource把打包产物注册为资源registerAppResource( server, resourceUri, resourceUri, { mimeType: RESOURCE_MIME_TYPE }, async () { const html await fs.readFile(path.join(DIST_DIR, mcp-app.html), utf-8); return { contents: [ { uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html, _meta: { ui: {} }, }, ], }; }, );关键点DIST_DIR path.join(import.meta.dirname, dist)即读取 Vite 构建产物目录中的mcp-app.html资源回调返回contents数组其中text字段携带完整的 HTML 字符串mimeType使用RESOURCE_MIME_TYPE注意工具与资源共用同一个resourceUri常量——这正是文档强调的「组件与工具通过resourceUri连接」的实现证据。如果继续阅读课程code目录下的完整版 server.ts可以看到 FAQ 工具的完整形态faq键值对数据、带zod.object({ query: zod.string().default(shipping) })的inputSchema以及registerAppToolregisterAppResource成对注册的模式——作业解答是这套模式的精炼版。前端 UImcp-app.html标记作业的 UI 标记位于 mcp-app.html满足作业全部三条 UI 要求!DOCTYPE html html langen head meta charsetUTF-8 / titleRock paper scissor/title /head body div classrock-paper-scissors h1Rock Paper Scissors/h1 select idrps-options valuerock option valuerockRock/option option valuepaperPaper/option option valuescissorsScissors/option /select button classselect idrps-buttonSelect/button pResult: code idrps-result.../code/p /div script typemodule src/src/mcp-app.ts/script /body /html对应作业验收点select idrps-options提供三个选项的下拉列表、button idrps-button是提交选择的按钮、code idrps-result是展示结果的标签。页面最后以模块方式引入/src/mcp-app.ts即事件绑定脚本。前端逻辑src/mcp-app.ts的事件绑定与工具调用assignment 版 mcp-app.ts 是整个交互的枢纽它完整演示了「获取元素引用 → 创建 App → 处理工具结果 → 绑定事件 → 连接 Host」的标准流程import { App } from modelcontextprotocol/ext-apps; // 获取元素引用 const serverTimeEl document.getElementById(server-time)!; // rps const getRpsBtn document.getElementById(rps-button)!; const rpsResponseEl document.getElementById(rps-result)!; const rpsOptions document.getElementById(rps-options) as HTMLSelectElement; // 创建 App 实例 const app new App({ name: Get Time App, version: 1.0.0 }); // 处理来自服务器的工具结果。必须在 app.connect() 之前设置 // 以免错过最初的工具结果。 app.ontoolresult (result) { const time result.content?.find((c) c.type text)?.text; serverTimeEl.textContent time ?? [ERROR]; }; getRpsBtn.addEventListener(click, async () { const userChoice rpsOptions.value; const result await app.callServerTool({ name: play-rps, arguments: { choice: userChoice } }); const rpsResult result.content?.find((c) c.type text)?.text; rpsResponseEl.textContent rpsResult ?? [ERROR]; }); // 连接到 Host app.connect();这段代码的核心机制new App({ name, version })来自modelcontextprotocol/ext-apps包封装了与父页面通信的协议细节app.callServerTool({ name: play-rps, arguments: { choice: userChoice } })是请求后端的关键调用——正如课程主文档所述它实际上向父窗口发送消息由父窗口Host转发并调用 MCP Server从返回结果中取出content里type text的文本渲染到rps-result没有结果时兜底显示[ERROR]app.ontoolresult在connect()之前赋值避免遗漏初始工具结果app.connect()放在最后完成与 Host 的连接握手。如何运行与验证作业文档给出的运行方式为参考 code/typescript/README.md 的完整工程再把作业三个文件的内容分别填入对应文件中。完整工程的运行步骤如下安装依赖并校验编译# 进入 my-app 目录 npm install # 同时安装前端与后端依赖 npx tsc --noEmit # 校验后端编译无输出即代表通过依赖清单可在 code 版 package.json 中看到运行时依赖modelcontextprotocol/ext-apps、modelcontextprotocol/sdk、express、cors开发依赖包括typescript、tsx、vite、vite-plugin-singlefile、concurrently、cross-env等要求 Node.js 20。启动后端npm start该命令由concurrently并行执行两件事cross-env NODE_ENVdevelopment INPUTmcp-app.html vite build --watch开发模式增量打包 UI和tsx watch main.ts热重启 MCP 服务端。启动后 MCP 端点位于http://localhost:3001/mcp。注意若在 Windows 上运行concurrently可能需要替换为等效工具若在 Codespace 中需要把端口可见性设为 public并通过https://Codespace 名称.app.github.dev/mcp验证端点可达。后端入口 main.ts 揭示了端点的实现方式使用createMcpExpressApp创建 Express 应用启用 CORS允许GET/POST/OPTIONS请求头含Content-Type、Authorization、MCP-Protocol-Version并在/mcp路由上为每个请求创建一次McpServer实例与StreamableHTTPServerTransport无会话 ID 的无状态模式。配套的 vite.config.ts 使用vite-plugin-singlefile把 UI 打包为单个 HTML 文件开发模式内联 sourcemap这正是registerAppResource能直接读取dist/mcp-app.html的原因。方案一在 Visual Studio Code 中测试在.vscode/mcp.json中注册服务器{ servers: { my-mcp-server-7178eca7: { url: http://localhost:3001/mcp, type: http } }, inputs: [] }点击mcp.json中的启动按钮在聊天窗口输入工具名课程示例为get-faq作业对应play-rps即可看到 MCP App 的 UI 渲染出来——这是目前测试 MCP Apps 最便捷的方式之一。方案二使用独立 Host 测试克隆ext-apps仓库后进入ext-apps目录执行npm install在另一个终端进入ext-apps/examples/basic-host执行npm startHost 会连接到后端并渲染 MCP App 界面。在 Codespace 环境中需要修改serve.ts中第 27 行的http://localhost:3001/mcp为你的 Codespace 后端地址形如https://name-3001.app.github.dev/mcp。验证游戏功能在渲染出的界面上从下拉框选择 Rock / Paper / Scissors点击 Select 按钮rps-result区域会显示类似「You win! You chose rock and I chose scissors.」的判定结果同时可通过点击「Call Tool」按钮查看工具返回的原始数据。从源码看 MCP Apps 的工作原理结合本作业与课程完整版代码可以提炼出 MCP Apps 的几条核心原理工具 资源成对注册resourceUri是唯一纽带registerAppTool的_meta.ui.resourceUri与registerAppResource的第一个参数指向同一个 URI如ui://get-time/mcp-app.htmlHost 据此把工具调用和 UI 渲染绑定。UI 在 IFrame 中运行出于安全考虑MCP App 的 HTML 被注入 IFrame 容器与父页面隔离。通信走消息转发IFrame 内的app.callServerTool()不直接发起网络请求而是向父窗口postMessage由 Host 代为调用 MCP Server 并回传结果——这是modelcontextprotocol/ext-apps等库封装的核心能力。数据与 UI 一同交付工具仍以标准 MCP 的content结构返回文本数据同时服务器附带可渲染的组件资源这正是「让 MCP Server 对数据如何呈现也有发言权」的范式落点。小结与关键收获本作业是对 MCP Apps 范式的完整练习服务端用registerAppToolregisterAppResource成对注册play-rps工具与其 UI 资源前端用mcp-app.html定义交互界面、src/mcp-app.ts负责事件绑定与callServerTool调用最终通过 VSCode 或独立 Host 验证游戏可玩。完成本作业后你已具备在 TypeScript 中构建和集成自有 MCP App 的能力可以继续深入 04-PracticalImplementation 章节把这些组件化能力应用到更完整的实际实现中。赞分享教程文档人工智能【免费下载链接】mcp-for-beginnersThis open-source curriculum introduces the fundamentals of Model Context Protocol (MCP) through real-world, cross-language examples in .NET, Java, TypeScript, JavaScript, Rust and Python. Designed for developers, it focuses on practical techniques for building modular, scalable, and secure AI workflows from session setup to service orchestration.项目地址https://gitcode.com/GitHub_Trending/mc/mcp-for-beginners点击查看免费下载相关推荐MCP Apps 实战作业用 TypeScript 构建带 UI 的石头剪刀布Rock-Paper-ScissorsMCP AppMCP Apps 实战作业用 TypeScript 构建带 UI 的石头剪刀布Rock Paper ScissorsMCP App 本篇文章基于 mcp教程文档人工智能MCP Apps 实战在 mcp-for-beginners 中用 TypeScript 构建带交互 UI 的石头剪刀布 MCP 应用MCP Apps 实战在 mcp for beginners 中用 TypeScript 构建带交互 UI 的石头剪刀布 MCP 应用 导读 本文围绕 mcp教程文档人工智能在 TypeScript 中构建剪刀石头布 MCP AppregisterAppTool 与 registerAppResource 实战在 TypeScript 中构建剪刀石头布 MCP AppregisterAppTool 与 registerAppResource 实战 MCP Apps教程文档人工智能创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

JS Tips 第 19 期:用 `String.prototype.concat` 实现类型安全的字符串拼接

JS Tips 第 19 期:用 `String.prototype.concat` 实现类型安全的字符串拼接

教程 【免费下载链接】jstips This is about useful JS tips! 项目地址: https://gitcode.com/gh_mirrors/js/jstips 点击查看 免费下载 本篇文章来自 jstips 仓库 Safe string concatenation(tip 19)这一期 JavaScript 技巧,核心…

2026/10/15 3:33:53 阅读更多 →
只留512专家的一半也能写代码?Qwen3.8-Flash-Next-GSQ-RCO-GGUF Coder代码特化版全解析

只留512专家的一半也能写代码?Qwen3.8-Flash-Next-GSQ-RCO-GGUF Coder代码特化版全解析

只留512专家的一半也能写代码?Qwen3.8-Flash-Next-GSQ-RCO-GGUF Coder代码特化版全解析 【免费下载链接】Qwen3.8-Flash-Next-GSQ-RCO-GGUF 项目地址: https://ai.gitcode.com/hf_mirrors/ISTA-DASLab/Qwen3.8-Flash-Next-GSQ-RCO-GGUF Qwen3.8-Flash-Next-…

2026/10/15 3:33:53 阅读更多 →
API管理系统选型实战:从网关到治理的完整评估框架

API管理系统选型实战:从网关到治理的完整评估框架

现在团队里谁还没被API折腾过?接口文档散落在各个群里、线上服务突然报错找不到对应网关策略、新同事入职一个月还没搞明白系统里有多少个内部服务——这些场景几乎每个研发团队都能对号入座。我最近半年密集参与了几次API管理系统选型评审,从开始的一头…

2026/10/12 15:52:13 阅读更多 →

最新新闻

Java接入个人微信API:消息回调进客服系统的正确姿势

Java接入个人微信API:消息回调进客服系统的正确姿势

Java 团队接个人微信,通常第一反应是:Spring Boot 写个 Controller,接收回调,然后直接在方法里写业务。 本地调试挺顺,一上生产就出现:接口超时、消息重复、Tomcat 线程打满。 问题不在 Java,而…

2026/10/15 3:33:41 阅读更多 →
YOLO26算法室内工作环境手部手势目标检测+训练好的模型+8212张数据集+pyqt可视化界面

YOLO26算法室内工作环境手部手势目标检测+训练好的模型+8212张数据集+pyqt可视化界面

YOLO26算法室内工作环境手部手势目标检测训练好的模型8212张数据集pyqt可视化界面 这套数据共 8212 张图。用 YOLO26 训了 100 轮,mAP50 0.7315,mAP50-95 0.6002。Precision 0.7335、Recall 0.7532。下面按数据构成 → 训练曲线 → 预测效果的顺序过一遍…

2026/10/15 3:33:41 阅读更多 →
仓颉编程语言 I/O 节点流完全指南:标准流与文件流的实战解析

仓颉编程语言 I/O 节点流完全指南:标准流与文件流的实战解析

文档知识库教程OpenHarmony 【免费下载链接】CangjieCorpus 本仓库为仓颉编程语言语料库,聚焦 仓颉编程语言 核心知识体系,整合官方开发指南、API 文档、典型代码示例及语法规范等多维度资源,经结构化处理与专业标注,构建为支持 R…

2026/10/15 3:33:41 阅读更多 →
LAMMPS bond_style mm3 键伸缩势详解:YAFF 包的 MM3 非谐势实现、参数配置与实战应用

LAMMPS bond_style mm3 键伸缩势详解:YAFF 包的 MM3 非谐势实现、参数配置与实战应用

科研科学计算高性能计算 【免费下载链接】lammps Public development project of the LAMMPS MD software package 项目地址: https://gitcode.com/gh_mirrors/la/lammps 点击查看 免费下载 导读 bond_style mm3 是 LAMMPS 中用于描述键伸缩运动的非谐势模型&…

2026/10/15 3:33:41 阅读更多 →
Flutter Engine Windows 无障碍实现解析:MSAA 与 UI Automation 在 AXPlatformNodeWin 中的落地

Flutter Engine Windows 无障碍实现解析:MSAA 与 UI Automation 在 AXPlatformNodeWin 中的落地

跨平台前端移动开发OpenHarmony 【免费下载链接】flutter_flutter 本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于…

2026/10/15 3:33:41 阅读更多 →
Natron ErodeSmooth 节点完全指南:归一化功率加权腐蚀/膨胀滤镜的参数解析与源码级解读

Natron ErodeSmooth 节点完全指南:归一化功率加权腐蚀/膨胀滤镜的参数解析与源码级解读

音视频视频处理图形学桌面应用 【免费下载链接】Natron Open-source video compositing software. Node-graph based. Similar in functionalities to Adobe After Effects and Nuke by The Foundry. 项目地址: https://gitcode.com/gh_mirrors/na/Natron 点击查看 …

2026/10/15 3:32:41 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →