Building mini program Skills with WeChat cloud development, you don't write wx.request, don't set up your own server, and don't maintain login state — wx.cloud.callFunction connects straight to cloud functions, with the user's OPENID injected automatically by cloud development's auth-free mechanism.
Opening
Many developers are paying attention to how to develop the newly released mini program Skills, so we quickly put together three out-of-the-box mini program SKILL templates:
- todolist-skill (to-do list): query, add, toggle complete, delete, reading directly from the cloud development database — the best entry point
- queue-skill (store queue ticketing): store search, queue status, online ticketing, queue progress
- drink-skill (coffee ordering): recommend/search drinks → pick size → fill address → order and pay, the complete ordering chain
Each template implements front-end and backend logic on WeChat cloud development, with complete SKILL.md business docs, interface-link diagrams, constraint rules and intent-routing examples, covering the most common mini program scenarios. Below we walk the full flow using todolist-skill.
1. What are mini program Skills
WeChat mini programs launched an AI development mode: you can wrap your existing mini program's features into capabilities the mini program AI can call in conversation. This capability unit is called a SKILL.
Unlike the old "user taps button → jumps page → fills form" flow, mini program Skills move the interaction forward into conversation:
[User] says in the mini program AI dialog: "记个待办:买牛奶"
↓
[Mini program AI] understands the intent, selects your addTodo capability, fills title="买牛奶"
↓
[Your code] executes the write, returns structured data
↓
[Mini program AI] pops a "to-do list card" you designed in the chat stream
You only do two things; the rest — which function to call, how to fill parameters, when to pop the card — is all AI:
- Write the feature as a function (officially called an atomic interface) — the code that actually does the work;
- Give the function a card (officially called an atomic component) that renders results as a GUI card in the chat stream.
The four-piece kit of a mini program Skill
A mini program SKILL lives in an independent subpackage with four core files:
| File | Purpose |
|---|---|
mcp.json | Declares to AI: what functions exist, how to fill parameters, which card to display |
apis/*.js | Atomic interfaces — the functions that actually work |
index.js | Registers the functions with the runtime |
components/ | Atomic components — cards for displaying results |
SKILL.md (optional) | Business SOP, telling AI the flow orchestration for this scenario |
Underneath is the mini program MCP protocol: the WeChat client runtime and the mini program AI backend interact over it. You don't need to understand the protocol details — just implement the SKILL fully per the spec.
Note: this mode is in internal beta and code submission isn't open yet. Don't merge related code into the official version for review.
2. The unavoidable question: what about login state
Atomic interfaces run in a separate JS environment inside the WeChat client, isolated from the mini program's main environment. When AI calls your function, you must figure out "who this user is" inside the function.
The traditional way: self-built backend + auth middleware
If your backend is a self-built server, the standard flow is:
- Call
wx.login()for a temporary code; - Send the code to your backend;
- Backend calls WeChat's code2session with code + AppSecret to exchange for openid and session_key;
- Backend generates a custom login token and returns it;
- Store the token in storage and attach it to subsequent requests;
- Multiple atomic interfaces all need this, so you write a middleware to handle it uniformly.
In code, it's like this — every Skill has to maintain this chunk:
// index.js — traditional self-built backend: must write middleware to maintain login state
const skill = wx.modelContext.createSkill('skills/todo-skill')
skill.registerAPI('addTodo', addTodo)
skill.use(async (ctx, next) => {
// unified login state: no token? run the token-exchange flow
let token = wx.getStorageSync('token')
if (!token) {
const { code } = await wx.login()
const res = await wx.request({
url: 'https://your-server.com/login', // your own backend
data: { code }
})
token = res.data.token
wx.setStorageSync('token', token)
}
await next()
})
Then the backend still needs code2session exchange logic, session_key storage, and token issuance and verification. This code has nothing to do with business — it's pure boilerplate for "knowing who the user is".
3. With WeChat cloud development: no login state, get openid directly
With WeChat cloud development as the backend, all that boilerplate can be deleted.
The atomic interface environment natively supports cloud development APIs (wx.cloud.init, wx.cloud.callFunction, wx.cloud.database all work directly). Cloud functions have a property: the caller's identity comes naturally through WeChat's underlying chain, and one line in the cloud function gets the current user's openid — no wx.login, no code2session, no self-built auth service, no middleware maintaining tokens.
| Step | Self-built backend | WeChat cloud development |
|---|---|---|
wx.login() for code | Required | Not needed |
| Backend code2session → openid | Implement yourself | Brought by the underlying chain |
| Issue / verify custom token | Required | Not needed |
| Middleware for login state | Must write | Not needed |
| Read openid server-side | A chain of conversions | cloud.getWXContext().OPENID one line |
4. Build a to-do mini program Skill
The goal is simple: users say "记一下" to add, "看看我的待办" to view, tap the card to complete.
Step 1: declare the subpackage and SKILL (app.json)
The SKILL must be in an independent subpackage, with on-demand injection and cloud development enabled:
{
"lazyCodeLoading": "requiredComponents",
"cloud": true,
"subPackages": [
{
"root": "skills/todo-skill",
"independent": true,
"pages": []
}
],
"agent": {
"skills": [
{
"name": "todo",
"description": "待办清单业 务",
"path": "skills/todo-skill"
}
]
}
}
Step 2: declare capabilities to AI (mcp.json)
The to-do scenario has three actions: add, view, complete. In the parameter descriptions, clearly state "where the value comes from" and "what to do when missing", and explicitly tell AI not to invent — this directly decides whether AI calls the right interface with the right parameters:
{
"apis": [
{
"name": "addTodo",
"description": "新增一条待办事项。当用户说『记一下/帮我记/加个待办』并给出具体内容时调用。",
"_meta": { "ui": { "componentPath": "components/todo-card/index" } },
"inputSchema": {
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "待办内容,取自用户原话(如『买牛奶』)。用户未说出具体内容时禁止填写,应反问『您要记什么待办?』。"
}
},
"required": ["title"]
}
},
{
"name": "listTodos",
"description": "查看当前用户的全部待办清单。当用户说『看看我的待办/还有什么没做』时调用,无需任何参数。",
"_meta": { "ui": { "componentPath": "components/todo-card/index" } },
"inputSchema": { "type": "object", "properties": {} }
},
{
"name": "completeTodo",
"description": "把一条待办标记为已完成。",
"_meta": { "ui": { "componentPath": "components/todo-card/index" } },
"inputSchema": {
"type": "object",
"properties": {
"todoId": {
"type": "string",
"description": "待办唯一标识,必须来自上游 listTodos 返回的 todoId 原值。禁止编造,也禁止从用户自然语言推断;上下文无可用 todoId 时应先调 listTodos。"
}
},
"required": ["todoId"]
}
}
],
"components": [
{ "path": "components/todo-card/index", "relatedPage": "/pages/index/index" }
]
}
The three interfaces share one todo-card for rendering. relatedPage is the page linked to the card's top-right "enter mini program" entry — fill in your mini program's real to-do page path.
Step 3: atomic interfaces call WeChat cloud development directly (index.js)
index.js runs in the atomic interface environment. Initialize wx.cloud then implement and register interfaces in place. Note: writes go through cloud functions, read-only operations hit the cloud database directly, and user identity is brought automatically by WeChat's underlying layer throughout:
// skills/todo-skill/index.js
wx.cloud.init({ env: 'your-env-xxxxxx' }) // swap in your cloud dev environment ID
const skill = wx.modelContext.createSkill('skills/todo-skill')
// add todo: write goes through a cloud function (OPENID auto-injected, no login)
skill.registerAPI('addTodo', async ({ title }) => {
if (!title) return { isError: true, content: [{ type: 'text', text: '缺少待办内容,请反问用户要记什么,禁止编造。' }] }
const { result } = await wx.cloud.callFunction({ name: 'addTodo', data: { title } })
return {
content: [{ type: 'text', text: `已添加「${title}」,请展示最新待办清单卡片。` }],
structuredContent: { todos: result.todos }
}
})
// view todos: read-only, hit the cloud database directly
skill.registerAPI('listTodos', async () => {
const { data } = await wx.cloud.database().collection('todos').orderBy('createTime', 'desc').get()
return {
content: [{ type: 'text', text: `共 ${data.length} 条待办,请展示清单卡片,禁止纯文本罗列。` }],
structuredContent: { todos: data.map(t => ({ todoId: t._id, title: t.title, done: t.done })) }
}
})
// complete todo: write goes through a cloud function
skill.registerAPI('completeTodo', async ({ todoId }) => {
if (!todoId) return { isError: true, content: [{ type: 'text', text: 'todoId 须来自 listTodos 返回值,禁止编造。' }] }
await wx.cloud.callFunction({ name: 'completeTodo', data: { todoId } })
return { content: [{ type: 'text', text: '已标记完成,请刷新待办清单卡片。' }] }
})
module.exports = skill
content is for AI — suggest a two-part "state the fact + indicate the next step" style; structuredContent is both for AI's understanding and passed to the component for rendering. The whole code has no skill.use(...) login-state boilerplate.
Step 4: one line to get openid in the cloud function (cloudfunctions/)
The cloud function natively knows who the user is. Writes persist in the cloud function, with ownership checks along the way:
// cloudfunctions/addTodo/index.js
const cloud = require('wx-server-sdk')
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
const db = cloud.database()
exports.main = async (event) => {
// ✨ auth-free: get the current user's openid directly, no wx.login, no code2session
const { OPENID } = cloud.getWXContext()
const title = String(event.title || '').slice(0, 100) // server-side re-validation
if (!title) return { code: 1, msg: '内容为空' }
await db.collection('todos').add({ data: { _openid: OPENID, title, done: false, createTime: db.serverDate() } })
const { data } = await db.collection('todos').where({ _openid: OPENID }).orderBy('createTime', 'desc').get()
return { code: 0, todos: data.map(t => ({ todoId: t._id, title: t.title, done: t.done })) }
}
// cloudfunctions/completeTodo/index.js
const cloud = require('wx-server-sdk')
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
const db = cloud.database()
exports.main = async (event) => {
const { OPENID } = cloud.getWXContext()
// include _openid in where, ensuring you can only change your own todo (ownership check)
await db.collection('todos').where({ _id: event.todoId, _openid: OPENID }).update({ data: { done: true } })
return { code: 0 }
}
AI-generated parameters can't be trusted. Auth-free solves "who the user is", but "does this todoId belong to this user" still needs the _openid in the where as the backstop.
Step 5: make a card (components/todo-card/index.js)
The component environment doesn't support wx.cloud; all data comes from the interface's structuredContent. When a user taps a to-do, completeTodo is triggered through a follow-up message on behalf of the user:
Component({
data: { todos: [] },
lifetimes: {
created() {
this.ctx = wx.modelContext.getContext(this)
// listen for atomic interface results, render the card
this.ctx.on(wx.modelContext.NotificationType.Result, ({ result }) => {
this.setData({ todos: (result.structuredContent || {}).todos || [] })
})
}
},
methods: {
onTapTodo(e) {
const { todoId, title } = e.currentTarget.dataset.todo
this.ctx.sendFollowUpMessage({
content: [
{ type: 'text', text: `完成「${title}」` },
{ type: 'api/call', data: { name: 'completeTodo', arguments: { todoId } } }
]
})
}
}
})
The corresponding index.wxml:
<view class="card">
<view wx:for="{{todos}}" wx:key="todoId" class="item {{item.done ? 'done' : ''}}"
data-todo="{{item}}" bindtap="onTapTodo">
{{item.done ? '✓' : '○'}} {{item.title}}
</view>
</view>
In DevTools, switch the compile mode to "mini program AI compile", type "帮我记个待办:买牛奶" in the dialog, and you'll see AI call addTodo and pop the to-do list card. Tapping a to-do auto-triggers completeTodo and refreshes the card.
5. Why WeChat cloud development and mini program Skills fit so well
1. A whole layer of login boilerplate is gone: atomic interfaces run in a separate JS environment; the traditional approach needs wx.login + self-built code2session + token middleware to identify users. cloud.getWXContext().OPENID gets it in one line in the cloud function. Every line you write is business — none of it is for figuring out who the user is.
2. No "login expired" problem: custom tokens expire, then need refreshing, with fallback logic in middleware. Cloud development's identity is carried natively at the request level — there's no "login expired, re-login" chain at all, and the conversation experience is smoother.
3. Backend capabilities out of the box: database, storage and scheduled triggers are right there in cloud functions:
const { OPENID } = cloud.getWXContext()
// read user-specific data, permission isolation by nature
const todos = await db.collection('todos')
.where({ _openid: OPENID })
.get()
The cloud database defaults data ownership by _openid; in the Skills scenario, users only see their own data — this permission isolation is nearly free, no extra AuthZ layer needed.
6. Summary
The dreariest and most error-prone part of connecting a mini program to AI conversation usually isn't the business logic — it's figuring out who the user is in an isolated environment.
- Mini program Skills let AI call your features directly in conversation; you just write "function + card";
- WeChat cloud development takes "identifying the user" off your plate — no middleware, no token exchange, one line of
getWXContext().OPENID.
Together, you can focus entirely on "what should this Skill actually help the user accomplish."
Appendix: reference links
- Mini program AI development mode official docs and sample project ai-mode-demo: https://github.com/wechat-miniprogram/ai-mode-demo
- To-do SKILL template (todolist-skill): https://github.com/TencentCloudBase/awesome-miniprogram-skills/tree/main/skills/todolist-skill
- Store queue SKILL template (queue-skill): https://github.com/TencentCloudBase/awesome-miniprogram-skills/tree/main/skills/queue-skill
- Coffee ordering SKILL template (drink-skill): https://github.com/TencentCloudBase/awesome-miniprogram-skills/tree/main/skills/drink-skill

