From 3b5502c8ec9a8e187d5e03c0f6fc4716452513d2 Mon Sep 17 00:00:00 2001 From: JTropy <195830459+JTropy@users.noreply.github.com> Date: Sun, 20 Sep 2026 11:47:32 +0800 Subject: [PATCH] =?UTF-8?q?feat(mcpapps):=20=E5=85=B1=E4=BA=AB=E5=9B=BE?= =?UTF-8?q?=E7=89=87=E4=B8=8A=E4=B8=8B=E6=96=87=E7=BB=84=E4=BB=B6=E4=B8=8E?= =?UTF-8?q?=E8=B5=84=E6=BA=90=E5=A5=91=E7=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 +++++ mcpapps/apps.go | 9 +++++ mcpapps/image.html | 76 +++++++++++++++++++++++++++++++++++++++ mcpapps/image.test.mjs | 82 ++++++++++++++++++++++++++++++++++++++++++ mcpapps/image_test.go | 26 ++++++++++++++ protocol.go | 4 +++ 6 files changed, 205 insertions(+) create mode 100644 mcpapps/image.html create mode 100644 mcpapps/image.test.mjs create mode 100644 mcpapps/image_test.go diff --git a/README.md b/README.md index b18963a..f38815d 100644 --- a/README.md +++ b/README.md @@ -8,3 +8,11 @@ The repository intentionally has two logical layers: - `mcpcontract` owns only the canonical model-facing MCP contracts shared by the two entrypoints: input/output schemas, annotations, and bounded behavior vectors. Neither package owns AgentDock runtime behavior, NexusDock stores, renderer HTML, Recall persistence, Workflow persistence, or HTTP handlers. Those remain in their application repositories. + +## 共享 MCP Apps + +独立的 `mcpapps` 包维护两个入口复用的 UI 文档。`HTML("view_image", ...)` 返回图片组件,资源身份与兼容契约由根包的 `ImageUIResourceURI` / `ImageUIContract` 定义。图片内容仍使用标准 MCP image 块,各入口自行注册资源和绑定工具,不将执行或 HTTP 逻辑放进共享包。 + +图片组件只预览本次工具结果,用户点击后通过 ChatGPT 宿主的 `uploadFile` 和 `widgetState.imageIds` 附加到后续对话。未提供宿主接口的客户端只显示预览;不保证同轮自动视觉,不读取额外文件、不使用 OCR、不默认保存到文件库,也不扩大 CSP 网络域名。 + +验证命令:`go test ./...`、`go test -race ./...`、`go vet ./...`、`node --test mcpapps/image.test.mjs`(Node 20+)。组件上传、宿主状态更新、错误、重复点击、换图竞争及非父 frame 消息均有行为测试。真实模型识图仍须在宿主中独立验收,不能仅凭单元测试推定。 diff --git a/mcpapps/apps.go b/mcpapps/apps.go index 4a571d6..aeeae43 100644 --- a/mcpapps/apps.go +++ b/mcpapps/apps.go @@ -8,8 +8,17 @@ import ( //go:embed app.html var appHTMLTemplate string +//go:embed image.html +var imageHTML string + +// ImageResultText explains the optional, user-initiated follow-up without claiming the model saw pixels. +const ImageResultText = "图片已返回。若客户端未将图片直接提供给模型,可在图片组件中将它附加到下一轮对话。" + // HTML renders one shared MCP App document for a known AgentDock view. // View and title are internal constants owned by AgentDock/NexusDock, not user input. func HTML(view, title string) string { + if view == "view_image" { + return imageHTML + } return strings.NewReplacer("{{VIEW}}", view, "{{TITLE}}", title).Replace(appHTMLTemplate) } diff --git a/mcpapps/image.html b/mcpapps/image.html new file mode 100644 index 0000000..4147958 --- /dev/null +++ b/mcpapps/image.html @@ -0,0 +1,76 @@ + + + +

等待图片

+ diff --git a/mcpapps/image.test.mjs b/mcpapps/image.test.mjs new file mode 100644 index 0000000..571bd67 --- /dev/null +++ b/mcpapps/image.test.mjs @@ -0,0 +1,82 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import vm from 'node:vm'; +import { webcrypto } from 'node:crypto'; + +const html = readFileSync(new URL('./image.html', import.meta.url), 'utf8'); +const source = html.match(/