MVP 边界定义为:
原子接口尽量做到小程序 MCP 契约级兼容;原子组件做到“小程序 MCP 原子组件运行时子集”兼容,不做完整小程序组件/页面运行时。
anp-miniapp-dock 完整继承小程序 MCP 的核心抽象:SKILL.md、mcp.json、原子接口、原子组件、content、structuredContent、_meta、sendFollowUpMessage 和 api/call。底层运行时由独立 Rust Runtime、QuickJS-NG、ANP Rust SDK、Render IR JSON 和 CardSpec fallback 实现;Flutter Renderer Adapter 属于后续宿主接入,不复刻微信账号、微信支付、云开发、完整页面路由或完整 WXML/WXSS。
MVP 拆成两个隔离 VM:
Atomic API VM
执行 index.js / apis/*.js
负责原子接口注册、中间件、参数校验和标准返回
网络、存储和会话能力通过 Rust host/adapter boundary 承载
Atomic Component VM
执行 components/*/index.js
驱动 WXML/WXSS 子集编译、Render IR 和卡片交互
两者不共享 JS 全局变量。原子接口、原子组件、动态组件分别使用不同上下文,符合小程序 MCP 的运行模型。
mcp.json
├─ apis[] → Atomic API VM
└─ components[] → Atomic Component VM
AtomicApiResult
├─ content → Agent / LLM 上下文
├─ structuredContent → Agent / LLM 上下文 + 组件渲染数据
└─ _meta → 仅组件私有数据,不进入模型上下文
P0 必须支持:
SKILL.md
mcp.json
index.js
apis/*.js
components/*/{index.js,index.wxml,index.wxss,index.json}
CommonJS require
典型目录:
coffee-skill/
SKILL.md
mcp.json
index.js
apis/
searchDrinks.js
confirmOrder.js
payOrder.js
components/
drink-list/
order-confirm/
payment-result/
| 能力 | MVP 策略 |
|---|---|
SKILL.md |
P0 读取,作为业务说明和 Agent instruction |
mcp.json.apis[] |
P0 支持 |
mcp.json.components[] |
P0 读取元数据和动态能力声明 |
index.js |
P0 执行 |
apis/*.js |
P0 通过受限 CommonJS 加载 |
components/* |
P0 支持运行时子集 |
| 多 Skill | P0 可先单 Skill,P1 支持多 Skill |
| 微信分包语义 | 不支持,只按目录加载 |
加载限制:
require只能加载 Skill 包内文件;- 禁止路径穿越;
- 禁止远程代码加载;
- 运行时不得修改
mcp.json; mcp.json原始字段必须保留,ANP 扩展字段使用_meta.anp或x_anp。
原子接口是 MVP 优先级最高的兼容面。P0 需要做到:
加载 Skill
注册原子接口
执行原子接口
运行 middleware
校验 inputSchema
返回标准 AtomicApiResult
建立 wx.request / storage / session host boundary
通过 ANP DID adapter 完成身份和网络请求边界
当前 Rust MVP 已完成 Skill/API VM 主线和 ANP adapter 边界。wx.request、storage、session/card API 的直接 JS 注入属于后续 runtime bridge 扩展,不影响 P0 coffee demo 通过 demo-server auth/business API 和 local Skill VM 完成端到端验证。
P0 必须支持:
{
"name": "searchDrinks",
"description": "搜索饮品",
"_meta": {
"ui": {
"componentPath": "components/drink-list/index"
}
},
"inputSchema": {},
"outputSchema": {}
}| 字段 | 支持策略 |
|---|---|
name |
P0 必须支持,唯一,并与 registerAPI 名称一致 |
description |
P0 必须支持,给 Agent 选择工具使用 |
inputSchema |
P0 必须支持,用于参数校验 |
outputSchema |
P0 读取,弱校验;失败输出 warning |
_meta.ui.componentPath |
P0 必须支持,用于组件绑定 |
其他 _meta |
P0 保留,只传给组件,不暴露给模型 |
P0 校验规则:
name必须唯一;name必须能在index.js中注册;inputSchema必须是 JSON object;componentPath如果存在,必须能在components[]或文件目录中找到;outputSchema校验失败不阻塞执行,但必须记录 warning。
P0 必须兼容:
const skill = wx.modelContext.createSkill('/path/to/skill')
skill.registerAPI('searchDrinks', searchDrinks)
skill.use(async (ctx, next) => {
await next()
})| API / 语义 | P0 |
|---|---|
wx.modelContext.createSkill(skillPath) |
支持 |
skill.registerAPI(name, handler) |
支持 |
skill.use(middleware) |
支持 |
| 多 middleware | 支持 |
| middleware 顺序 | 注册顺序,洋葱模型 |
ctx.name |
支持 |
ctx.skillPath |
支持 |
ctx.arguments |
支持 |
next() |
支持 |
| middleware 捕获异常 | 支持 |
| middleware + handler 共享超时 | 支持,默认 300 秒 |
不支持:
跨 Skill 注册 API
动态注册远程 API
运行时修改 mcp.json
P0 支持:
string
number
boolean
object
array
enum
required
default
description
P1 支持:
format: image
format: file
P0 暂时只支持文本和结构化参数。图片/文件格式可以先在 schema 中保留,但运行时返回 unsupported_format 或进入 P1。
P0 必须严格支持:
interface AtomicApiResult {
isError?: boolean
content: TextContent[]
structuredContent?: Record<string, unknown>
_meta?: Record<string, unknown>
}
interface TextContent {
type: "text"
text: string
}执行规则:
| 返回字段 | 行为 |
|---|---|
isError: true |
不渲染组件,只把 content 返回给 Agent |
content |
必须存在,进入 Agent / LLM 上下文 |
structuredContent |
进入 Agent 上下文,也传给组件 |
_meta |
仅传给组件,不进入模型上下文 |
| 绑定组件且非错误 | 尝试渲染组件 |
| 组件渲染失败 | fallback 到 CardSpec |
| 无组件绑定 | 使用 structuredContent 生成通用卡片或纯文本 |
当前 Rust MVP 的 P0 直接 JS 支持与 host/adapter 边界:
| 分类 | API | 实现方式 |
|---|---|---|
| Skill | wx.modelContext.createSkill |
QuickJS Host Bridge |
| Skill | skill.registerAPI |
Runtime registry |
| Skill | skill.use |
Middleware chain |
| Session | wx.modelContext.getSessionId |
host boundary 已实现;JS 注入待后续扩展 |
| Card | wx.modelContext.expireAllCards |
host boundary 已实现;JS 注入待后续扩展 |
| 网络 | wx.request |
wx-compat RequestBroker + anp-adapter 已实现;JS 注入待后续扩展 |
| 存储 | wx.getStorage / setStorage |
scoped storage 已实现;JS 注入待后续扩展 |
| 文件 | wx.uploadFile / downloadFile / openDocument |
P0 未直接实现,保留为后续 host capability |
| 系统 | wx.getDeviceInfo / getAppBaseInfo |
host/component helper 已实现 |
P0 替代实现:
| 微信 API | ANP Runtime 实现 |
|---|---|
wx.login |
ANP DID 登录 |
wx.checkSession |
capability token 校验 |
wx.requestPayment |
Payment Intent mock + 用户确认 |
wx.getPhoneNumber |
MVP mock,后续手机号凭证 |
wx.chooseAddress |
MVP mock,后续宿主地址选择器 |
wx.getLocation |
后续可接宿主授权或 mock |
wx.getFuzzyLocation |
后续可接宿主授权或 mock |
P1 可支持:
| 分类 | API |
|---|---|
| 网络 | WebSocket 子集 |
| 位置 | wx.chooseLocation |
| 媒体 | wx.chooseMedia |
| 媒体 | wx.previewMedia |
| 通知 | wx.requestSubscribeMessage 映射到宿主通知授权 |
| 电话 | wx.makePhoneCall |
| 扫码 | wx.scanCode |
不支持 / 不建议支持:
wx.cloud.*
微信原生支付族 API
微信支付分
微信运动
发票
广告
公众号
视频号
客服
跳转其他小程序
WiFi
蓝牙
TCP
UDP
mDNS
传感器
完整地图能力
人脸核身
每个原子接口调用都必须携带运行上下文:
interface ApiCallContext {
userDid: string
agentDid: string
merchantDid: string
skillId: string
sessionId: string
apiName: string
arguments: Record<string, unknown>
capabilityToken?: string
}执行限制:
- 每次调用独立 QuickJS context;
- storage host boundary 必须按 DID、merchant 和 Skill 隔离;
wx.requesthost boundary 必须走 allowlist;- 默认禁止访问文件系统;
- 默认禁止
eval; - 默认禁止远程代码加载;
- 默认超时 300 秒;
- 高风险 API 必须先走 human authorization。
风险分级:
| 等级 | 例子 | 策略 |
|---|---|---|
| L0 查询 | 搜索商品、查询天气 | 可自动执行 |
| L1 个性化查询 | 查订单、查资产 | 需要登录 |
| L2 写操作 | 加购物车、改规格 | 可执行,但需记录 |
| L3 交易动作 | 下单、支付、退款 | 必须用户确认 |
| L4 隐私动作 | 手机号、地址、证件 | 必须用户确认 + 审计 |
原子组件目标是:
支持小程序 MCP 原子组件运行时子集
支持组件 JS 生命周期
支持 WXML/WXSS 子集
支持结构化数据输出 Render IR JSON 或 fallback CardSpec
支持用户点击后继续驱动 Agent 流程
推荐描述为:MVP 支持 MiniApp MCP Atomic Component Runtime Subset。不要承诺完整兼容微信原子组件、完整 WXML/WXSS 或完整微信小程序组件。
组件目录:
components/order-confirm/
index.js
index.wxml
index.wxss
index.json
| 文件 / 能力 | P0 策略 |
|---|---|
index.js |
QuickJS Component VM 执行 |
index.wxml |
WXML 子集解析 |
index.wxss |
WXSS 子集解析 |
index.json |
读取基础配置,弱支持 |
| 自定义组件嵌套 | 不支持 |
behaviors |
不支持 |
relations |
不支持 |
slots |
不支持 |
P0 支持:
Component({
properties: {
title: String
},
data: {
selected: null
},
lifetimes: {
created() {},
attached() {},
detached() {}
},
methods: {
onTap(e) {
this.setData({ selected: e.currentTarget.dataset.id })
}
}
})| 能力 | P0 |
|---|---|
Component({}) |
支持 |
data |
支持 |
properties |
支持基础类型 |
methods |
支持 |
lifetimes.created |
支持 |
lifetimes.attached |
支持 |
lifetimes.detached |
支持 |
this.setData() |
支持 |
this.data |
支持 |
this.properties |
支持 |
this.triggerEvent() |
P1 |
observers |
P2 |
behaviors |
不支持 |
relations |
不支持 |
externalClasses |
不支持 |
pageLifetimes |
不支持 |
options |
P2 |
slots |
不支持 |
P0 生命周期:
created
→ 注入 modelContext/viewContext
→ 绑定 input/result/expire 事件
attached
→ 首次生成 Render IR
→ 输出 Render IR JSON 或交给宿主渲染器
setData
→ 更新 Component State
→ 重新计算 WXML binding
→ diff Render IR
→ Render IR JSON 刷新或宿主渲染器刷新
detached
→ 清理事件监听
P0 支持示例:
<view class="card">
<text>{{title}}</text>
<image src="{{cover}}" bindload="onImageLoad" binderror="onImageError" />
<scroll-view scroll-x>
<view wx:for="{{items}}" wx:key="id" data-id="{{item.id}}" bindtap="onSelect">
<text>{{item.name}}</text>
</view>
</scroll-view>
<button bindtap="onConfirm">确认</button>
</view>| 能力 | P0 |
|---|---|
view |
支持 |
text |
支持 |
image |
支持 |
button |
支持 |
scroll-view |
支持横向 |
wx:if |
支持 |
wx:elif / wx:else |
P1 |
wx:for |
支持 |
wx:key |
支持基础 |
{{path}} 数据绑定 |
支持 |
| 简单表达式 | P1 |
bindtap |
支持 |
catchtap |
P1 |
data-* |
支持 |
class |
支持 |
style |
支持 |
template/import/include |
不支持 |
slot |
不支持 |
| 自定义组件嵌套 | P2 |
P0 表达式只支持:
{{foo}}
{{user.name}}
{{items.length}}
{{index}}
{{item.name}}
复杂表达式需要开发者在 JS 中通过 setData 计算好字段。P0 不支持三元表达式、算术表达式、逻辑默认值或函数调用。
P0 支持示例:
.card {
display: flex;
flex-direction: column;
padding: 12px;
background-color: #ffffff;
border-radius: 12px;
}
.title {
font-size: 16px;
font-weight: bold;
color: #111111;
}| 能力 | P0 |
|---|---|
| class 选择器 | 支持 |
| id 选择器 | P1 |
| 标签选择器 | P1 |
| 后代选择器 | P1 |
| flex 布局 | 支持 |
| width / height | 支持 |
| min / max width / height | 支持 |
| margin / padding | 支持 |
| color | 支持 |
| background-color | 支持 |
| font-size | 支持 |
| font-weight | 支持 |
| line-height | 支持 |
| text-align | 支持 |
| border | 支持 |
| border-radius | 支持 |
| opacity | 支持 |
| display none/block/flex | 支持 |
| rpx | 支持,映射到 host logical pixels |
| vw | P1 |
| media query | P2 |
| animation / transition | 不支持 |
| transform | P2 |
| box-shadow | P1 |
| 自定义字体 | 不支持 |
| filter / mask | 不支持 |
P0 只追求交易型卡片视觉足够用,不追求 CSS 完整性。
P0 必须支持:
| 组件 | 支持范围 |
|---|---|
view |
基础容器、flex、点击区域 |
text |
文本展示,不支持 user-select |
image |
网络图片、png/jpg/webp 可选 |
button |
普通按钮,不支持 open-type |
scroll-view |
仅横向滚动 |
P1 支持:
| 组件 | 支持范围 |
|---|---|
map |
MapPreview,不支持拖拽缩放 |
canvas |
静态绘制或降级图片 |
input |
表单场景需要时加入 |
textarea |
表单场景需要时加入 |
radio |
可用宿主组件替代 |
checkbox |
可用宿主组件替代 |
picker |
规格选择可用 BottomSheet 替代 |
不支持:
video
swiper
navigator
web-view 作为组件
ad
ad-custom
functional-page-navigator
所有微信社交 open-type
P0 支持:
| API | 策略 |
|---|---|
wx.modelContext.getContext(this) |
支持 |
wx.modelContext.getViewContext(this) |
支持 |
modelCtx.on(NotificationType.Input) |
支持 |
modelCtx.on(NotificationType.Result) |
支持 |
viewCtx.on(NotificationType.Expire) |
支持 |
viewCtx.getDimensions() |
支持 |
viewCtx.setRelatedPage({ path, query }) |
记录元数据,MVP fallback |
viewCtx.expirePreviousCards() |
支持 |
wx.modelContext.expireAllCards() |
支持 |
sendFollowUpMessage() |
支持 |
wx.getStorage / setStorage |
scoped storage host boundary 已实现;组件 JS 直调待后续扩展 |
wx.getDeviceInfo |
支持 |
wx.getAppBaseInfo |
支持 |
P1 支持:
| API | 策略 |
|---|---|
viewCtx.openDetailPage() |
BottomSheet / WebView fallback |
viewCtx.preloadDetailPage() |
no-op 或预加载 WebView |
viewCtx.on(NotificationType.Overflow) |
P1 |
wx.previewMedia |
P1 |
wx.makePhoneCall |
P1 |
默认不支持,除非声明 dynamic:
wx.request
setTimeout
setInterval
WebSocket
如果 mcp.json.components[].permissions.scope.dynamic 存在,P1 可开放:
wx.request
setTimeout
setInterval
clearTimeout
clearInterval
动态组件限制:
- 请求域名必须命中 allowlist;
- 限制最长运行时间;
- 卡片销毁时清理 timer;
- 卡片过期时停止动态请求;
- 宿主后台时暂停轮询。
P0 支持:
| 事件 | P0 |
|---|---|
tap |
支持 |
image load |
支持 |
image error |
支持 |
Expire |
支持 |
Input |
支持 |
Result |
支持 |
P1 支持:
longpress
touchstart / touchend,谨慎
Overflow
不支持:
复杂手势
动画事件
滚动纵向事件
输入法复杂事件
页面级事件
标准数据流:
用户消息
↓
Agent 选择 API
↓
Atomic API VM 执行 handler
↓
返回 AtomicApiResult
↓
如果 isError=true:
content → Agent 回复
↓
如果 isError=false 且有 componentPath:
structuredContent + _meta → Atomic Component VM
↓
Component VM 生成 Render IR
↓
Render IR JSON 交给宿主渲染器
↓
用户点击组件
↓
sendFollowUpMessage 或 api/call
↓
Agent 继续下一步
接口到组件输入:
interface ComponentRenderInput {
apiName: string
arguments: Record<string, unknown>
result: {
content: TextContent[]
structuredContent?: Record<string, unknown>
_meta?: Record<string, unknown>
}
}组件动作输出:
type ComponentAction =
| {
type: "sendFollowUpMessage"
content: ContentBlock[]
}
| {
type: "api/call"
name: string
arguments: Record<string, unknown>
}
| {
type: "openDetailPage"
url: string
}
| {
type: "expirePreviousCards"
componentPaths?: string[]
match?: "all" | "latest"
}所有 api/call 必须回到统一 Orchestrator 调用链,不能由组件直接绕过 inputSchema、权限、consent、middleware 或审计。
WXML AST 不直接驱动宿主 UI。当前 Rust MVP 渲染链路是:
WXML + WXSS + Component State
↓
Render IR
↓
Render IR JSON / host renderer adapter
P0 Render IR:
interface RenderNode {
id: string
type: "view" | "text" | "image" | "button" | "scroll-view"
props: Record<string, unknown>
style: StyleObject
events?: Record<string, EventBinding>
children?: RenderNode[]
}Render IR 的作用:
- 让后续 Flutter Renderer 更简单;
- 未来可支持 Web Renderer;
- 可做快照测试;
- 可做组件调试器;
- 可在渲染失败时 fallback 到 CardSpec。
P0 原子接口:
Skill Loader
mcp.json.apis[]
inputSchema 校验
index.js / apis require
createSkill
registerAPI
use middleware
AtomicApiResult
wx.request + ANP DID host boundary
scoped storage host boundary
getSessionId host boundary
expireAllCards host boundary
错误处理
超时控制
日志捕获
P0 原子组件:
componentPath 解析
components[] 元数据
Component({})
data/properties/methods
created/attached/detached
setData
getContext/getViewContext
Input/Result/Expire
view/text/image/button/scroll-view
wx:if/wx:for
{{path}} binding
bindtap
基础 WXSS
Render IR
Render IR JSON
sendFollowUpMessage
api/call
expirePreviousCards
CardSpec fallback
P1 原子接口:
format:image/file
chooseMedia
previewMedia
scanCode
makePhoneCall
chooseAddress 真实现
getPhoneNumber 真实现
Payment Intent 真实现
WebSocket
P1 原子组件:
openDetailPage fallback
preloadDetailPage
Overflow
scope.dynamic
wx.request in component
timer
map preview
canvas static
input/radio/checkbox/picker
不做:
完整微信小程序运行时
完整页面路由
完整半屏小程序页面
完整 WXML/WXSS
完整自定义组件系统
微信云开发
微信原生支付
微信社交生态 API
设备底层复杂 API
使用咖啡点单 demo 验收三张组件。
对应接口:searchDrinks
验证能力:
wx:for
image
text
scroll-view 横向
bindtap
sendFollowUpMessage
api/call
对应接口:confirmOrder
验证能力:
订单结构化展示
价格展示
button
humanAuthorization
expirePreviousCards
对应接口:payOrder
验证能力:
状态展示
错误/成功分支
卡片过期
纯文本 fallback
P0:完整支持原子接口契约与执行主线;
P0:支持原子组件运行时最小子集;
P0:不支持完整微信小程序组件和页面;
P0:当前 Rust MVP 渲染链路采用 Component VM → WXML/WXSS 子集 → Render IR JSON;
P0:Flutter Renderer Adapter 属于后续宿主接入;
P0:失败时 fallback 到 CardSpec / structuredContent / content。
这个边界比纯 CardSpec 更有小程序 MCP 生态兼容价值,同时避免进入完整微信小程序 Runtime 的实现范围。