Write Your First AI SKILL
Scenario
You want to package a business feature into an AI-callable SKILL, but there's no ready-made template to use. This tutorial walks through the full process from scaffold to working SKILL, using a "weather query" example.
Prerequisites
- Completed Build an AI-Powered Mini Program — project runs in DevTools
- WeChat DevTools Nightly build installed
- Base library ≥ 3.16.1
- Node.js ≥ 18
Steps
Step 1: Create the SKILL scaffold
npx mp-skills create weather-skill
Expected output:
* 创建 Skill: weather-skill
ok 目录已创建: miniprogram/skills/weather-skill/
ok SKILL.md 已创建
ok mcp.json 已创建
ok index.js 已创建
ok apis/ 目录已创建
ok app.json 已更新
ok project.config.json 已更新
[OK] Skill 创建完成
Generated structure:
miniprogram/skills/weather-skill/
├── SKILL.md # Business description — AI reads this to decide when to trigger
├── mcp.json # API declaration — defines parameters and return values
├── index.js # Entry point — registers the atomic API
└── apis/
└── getWeather.js # API implementation
Step 2: Write SKILL.md
# Weather Query
## Trigger Scenarios
User wants to check weather. Examples:
- "What's the weather like in Beijing today?"
- "Will it rain tomorrow?"
- "Shanghai temperature this weekend"
- "Check the weather in Shenzhen for the next 3 days"
## Out of Scope
- Date or time queries
- Image/video content requests
Step 3: Configure mcp.json
Write miniprogram/skills/weather-skill/mcp.json:
{
"apis": [
{
"name": "getWeather",
"description": "Get weather for a specified location. Call when user provides a city name or location. Requires location and optional days parameters.",
"inputSchema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "Location to query, e.g. city name (Beijing, Shanghai). Source: user's original message."
},
"days": {
"type": "number",
"description": "Forecast days, 1-7, defaults to 1."
}
},
"required": ["location"]
},
"outputSchema": {
"type": "object",
"properties": {
"location": { "type": "string" },
"forecasts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"date": { "type": "string" },
"weather": { "type": "string" },
"tempHigh": { "type": "number" },
"tempLow": { "type": "number" }
}
}
}
}
},
"_meta": {
"ui": { "componentPath": "components/weather-card/index" }
}
}
],
"components": [
{
"path": "components/weather-card/index",
"relatedPage": "/pages/index/index"
}
]
}
Step 4: Implement the atomic API
Write miniprogram/skills/weather-skill/apis/getWeather.js:
async function getWeather({ location, days = 1 }) {
if (!location) {
return {
isError: true,
content: [{ type: 'text', text: 'Missing location parameter. Please provide a city name.' }]
}
}
try {
const res = await wx.request({
url: 'https://api.example.com/weather',
data: { city: location, days: Math.min(days, 7) }
})
return {
isError: false,
content: [{ type: 'text', text: `Weather forecast for ${location} (${days} days)` }],
structuredContent: {
location,
forecasts: res.data.forecasts.map(f => ({
date: f.date,
weather: f.weather,
tempHigh: f.tempMax,
tempLow: f.tempMin
}))
}
}
} catch (err) {
return {
isError: true,
content: [{ type: 'text', text: `Weather query failed: ${err.message}. Please try again.` }]
}
}
}
module.exports = getWeather
Step 5: Create the atomic component
Create miniprogram/skills/weather-skill/components/weather-card/ with 4 files:
index.wxml:
<view class="card">
<text class="title">{{location}} Weather</text>
<view wx:for="{{forecasts}}" wx:key="date" class="day-row">
<text class="date">{{item.date}}</text>
<text class="weather">{{item.weather}}</text>
<text class="temp">{{item.tempLow}}°~{{item.tempHigh}}°</text>
</view>
</view>
index.js:
Component({
lifetimes: {
created() {
const modelCtx = wx.modelContext.getContext(this)
modelCtx.on(wx.modelContext.NotificationType.Result, (data) => {
const sc = data?.result?.structuredContent || {}
this.setData({
location: sc.location || '',
forecasts: sc.forecasts || []
})
})
}
}
})
index.json: { "component": true, "usingComponents": {} }
index.wxss:
.card { padding: 16px; background: #fff; border-radius: 12px; }
.title { font-size: 16px; font-weight: 600; margin-bottom: 12px; }
.day-row { display: flex; justify-content: space-between; padding: 8px 0; }
.date { font-size: 14px; color: #333; }
.weather { font-size: 14px; color: #666; }
.temp { font-size: 14px; color: #1677ff; }
Step 6: Register the API
Write miniprogram/skills/weather-skill/index.js:
const getWeather = require('./apis/getWeather')
function registerAPIs() {
const skill = wx.modelContext.createSkill('skills/weather-skill')
skill.registerAPI('getWeather', getWeather)
}
registerAPIs()
Step 7: Validate
npx mp-skills validate
Step 8: Test in DevTools
Switch to "Mini Program AI Compile" mode:
- SKILL list should show
weather-skill - Select it to see the
getWeatherAPI - Enter
{"location": "Beijing", "days": 3}and execute - Verify the weather card renders correctly
Verification Checklist
-
npx mp-skills validatepasses -
getWeatherAPI works in DevTools - Weather data renders via
weather-cardcomponent - Missing
locationreturns error message
Common Issues
skill.registerAPI name mismatch
- Check that
mcp.json'sapis[].namematches the name in your code
API called but no result returned
- Check
module.exportsis present - Check
requirepath inindex.js
Component shows no data
- Check
componentPathinmcp.json - Check
structuredContentfield names match the atomic API output