Hands-On: Give Your Hardware a Brain — Build MCP Skills That Let AI Control Real Devices
Imagine telling your oven "Preheat to 200 degrees and bake for 25 minutes" — and it just... does it. No app, no buttons, no menu diving. Just natural language, understood by an AI agent, executed on real hardware.
This guide shows you exactly how that works, using a Smart Oven project built with TuyaOpen. By the end, you'll know how to create your own hardware MCP skills for any device.
What You'll Build
An AI-powered Smart Oven where a chat agent can:
| Command | What the Agent Does |
|---|---|
| "Turn on the oven" | Calls oven.start → sets the power DP |
| "Preheat to 200°C" | Calls oven.set_temperature(200) → writes the temp DP |
| "Bake for 25 minutes" | Calls oven.set_timer(1500) → sets countdown DP |
| "Is it still on?" | Calls oven.get_state() → reads all DPs, returns JSON |
| "Run the pizza recipe" | Calls oven.run_recipe("pizza") → sets temp + timer + starts |
| "Take a photo and check if the cake is done" | Calls device.camera_shot() → captures a JPEG the AI can see |
The magic glue? MCP Function Call — the protocol that turns hardware operations into tools an AI agent can discover and invoke.
How It Works: The Architecture
The key insight: Every hardware capability (set temperature, read state, capture image) is registered as an MCP tool — a named function with typed parameters and a callback. The AI agent sees these tools, decides which to call based on user intent, and the callback drives the real hardware.
Step 1: Create the Cloud Product
Before writing any firmware code, you need a product on the Tuya cloud platform. The product defines your device's data model (DPs), AI agent, and cloud capabilities. Everything downstream — firmware, MCP tools, the app — depends on this.
1a. Create the product
- Log in to the Tuya Developer Platform → AI Product > Development → Create
- Select Custom to create a custom product from scratch (instead of picking a preset category)
- Complete the creation wizard — you'll get a PID (Product ID)
The fastest way: use the /tuya-iot-platform vibe coding skill in TuyaOpen IDE. Just describe your device in natural language and the Agent creates the product, defines DPs, and configures the AI Agent for you.
1b. Define Data Points (DPs)
Data Points (DPs) are the digital twin of your hardware. Each DP maps to a controllable or readable feature. In the Function Definition tab, click Add to create these custom DPs (IDs 101–199):
| DP ID | Code | Type | Range | Description |
|---|---|---|---|---|
| 101 | switch | Bool | — | Power on/off |
| 102 | temp_set | Value | 50–250 | Target temperature (°C) |
| 103 | temp_current | Value | 0–300 | Current temperature (°C) |
| 104 | timer | Value | 0–3600 | Countdown timer (seconds) |
Standard DPs (ID < 100) are pre-defined by Tuya. Custom DPs (ID 101–199) are yours to define. For the oven, all four are custom.
1c. Add AI Agent & MCP capabilities
In the Function Definition tab → Product AI Capabilities → Add Agent:
- Model Configuration → Skills Configuration → select Plugin → add Device Control · Bound Only (this enables MCP tool calls to your device DPs)
- Prompt Development → write a system prompt that describes the oven's capabilities so the AI knows when to call each tool
This creates the cloud-side Agent that will discover and invoke the MCP tools you register in firmware.
1d. Generate the DP header for firmware
Once DPs are defined on the cloud, generate the C header your firmware will include:
tuyaopen dp generate --target embedded
This produces tuya_dp_profile.h — the contract between cloud and device:
// tuya_dp_profile.h — auto-generated by `tuyaopen dp generate`
#define DPID_SWITCH 101
#define DPID_TEMP_SET 102
#define DPID_TEMP_CURRENT 103
#define DPID_TIMER 104
#define DPID_TEMP_SET_MIN 50
#define DPID_TEMP_SET_MAX 250
#define DPID_TIMER_MIN 0
#define DPID_TIMER_MAX 3600
The full product creation flow is documented in Create Your Product & Agent. For the oven demo, you can also let the AI Agent generate the product and DPs for you using the /tuya-iot-platform skill in TuyaOpen IDE.
Step 2: Implement the Mock Hardware Layer
Before wiring up MCP tools, you need functions that simulate (or drive) the actual hardware. This keeps your tool callbacks clean:
// app_oven.h
typedef struct {
bool switch_on;
int temp_set;
int temp_current;
int timer_remaining;
} oven_state_t;
OPERATE_RET app_oven_set_switch(bool on);
OPERATE_RET app_oven_set_temp(int temp_c);
OPERATE_RET app_oven_set_timer(int seconds);
OPERATE_RET app_oven_add_timer(int seconds);
oven_state_t app_oven_get_state(void);
// app_oven.c — each setter reports DPs to the cloud + updates the LCD
static oven_state_t g_oven_state = {
.switch_on = false,
.temp_set = 180,
.temp_current = 25,
.timer_remaining = 0
};
OPERATE_RET app_oven_set_temp(int temp_c) {
if (temp_c < DPID_TEMP_SET_MIN || temp_c > DPID_TEMP_SET_MAX)
return OPRT_INVALID_PARM;
g_oven_state.temp_set = temp_c;
__oven_report_dp(); // sync to cloud + LCD
return OPRT_OK;
}
Step 3: Register MCP Tools (The Core Step)
This is where the AI agent gets its "hands" on your hardware. Each tool is registered with:
- Name — what the AI sees (use dot-notation:
oven.set_temperature) - Description — LLM-friendly text explaining when/how to call it
- Parameters — typed input properties with ranges
- Callback — the function that runs when the AI calls this tool
The Registration Pattern
#include "ai_mcp_server.h"
#include "tal_event_info.h"
// Called once when MQTT connects (deferred registration)
static OPERATE_RET __oven_mcp_on_mqtt_connected(void *data) {
(void)data;
// Tool 1: Start the oven
TUYA_CALL_ERR_GOTO(AI_MCP_TOOL_ADD(
"oven.start",
"Turn on the oven. Use when the user wants to start cooking, "
"preheat, or begin baking.\nParameters: none\nReturns: bool",
__oven_start_cb, NULL
));
// Tool 2: Set temperature
TUYA_CALL_ERR_GOTO(AI_MCP_TOOL_ADD(
"oven.set_temperature",
"Set the oven target temperature in Celsius (50-250).\n"
"Parameters: temperature (int)\nReturns: int (applied temp)",
__oven_set_temp_cb, NULL,
MCP_PROP_INT_RANGE("temperature", "Target temperature in °C (50-250).",
DPID_TEMP_SET_MIN, DPID_TEMP_SET_MAX),
MCP_PROP_END
));
// Tool 3: Get full state
TUYA_CALL_ERR_GOTO(AI_MCP_TOOL_ADD(
"oven.get_state",
"Get the current oven state: power, target temp, current temp, "
"timer remaining.\nParameters: none\nReturns: JSON object",
__oven_get_state_cb, NULL
));
// ... more tools
return OPRT_OK;
}
OPERATE_RET app_oven_mcp_init(void) {
return tal_event_subscribe(
EVENT_MQTT_CONNECTED, "oven_mcp_tools",
__oven_mcp_on_mqtt_connected, SUBSCRIBE_TYPE_ONETIME);
}
The Callback Pattern
Each callback reads AI-supplied arguments from properties, calls the hardware function, and returns a result:
static OPERATE_RET __oven_set_temp_cb(const MCP_PROPERTY_LIST_T *properties,
MCP_RETURN_VALUE_T *ret_val,
void *user_data) {
// 1. Read the AI-supplied parameter
int temp = properties->properties[0]->value.int_val;
// 2. Call the hardware function
OPERATE_RET rt = app_oven_set_temp(temp);
// 3. Return the result to the AI
ai_mcp_return_value_set_int(ret_val,
(rt == OPRT_OK) ? temp : -1);
return OPRT_OK;
}
For JSON returns (like get_state):
static OPERATE_RET __oven_get_state_cb(const MCP_PROPERTY_LIST_T *properties,
MCP_RETURN_VALUE_T *ret_val,
void *user_data) {
oven_state_t s = app_oven_get_state();
cJSON *json = cJSON_CreateObject();
cJSON_AddBoolToObject(json, "switch_on", s.switch_on);
cJSON_AddNumberToObject(json, "temp_set", s.temp_set);
cJSON_AddNumberToObject(json, "temp_current", s.temp_current);
cJSON_AddNumberToObject(json, "timer_remaining", s.timer_remaining);
ai_mcp_return_value_set_json(ret_val, json);
return OPRT_OK;
}
Step 4: Wire It Into the Boot Sequence
In your app_chat_bot.c, call your MCP init right after ai_mcp_init():
#if defined(ENABLE_COMP_AI_MCP) && (ENABLE_COMP_AI_MCP == 1)
TUYA_CALL_ERR_RETURN(ai_mcp_init());
TUYA_CALL_ERR_RETURN(app_oven_mcp_init()); // ← your tools
#endif
Tools register automatically when MQTT connects. That's it.
Step 5: Build and Test
cd source/embedded
tos.py build
Verify the tool appears in the agent's tool list, then try these interactions:
| You Say | Agent Calls | Result |
|---|---|---|
| "Preheat to 200 and bake for 25 minutes" | oven.set_temperature(200) → oven.set_timer(1500) → oven.start() | Oven heats up, timer counts down |
| "Roast a chicken" | oven.run_recipe("roast") | 200°C, 40 min, auto-start |
| "Is it still on? How hot?" | oven.get_state() | Returns {switch_on: true, temp_set: 200, ...} |
| "Take a photo and check if the cake is done" | device.camera_shot() | AI receives a JPEG and can visually inspect |
The Complete Tool Set
Here's the full set of MCP tools for the Smart Oven:
| Tool | Parameters | Returns | Purpose |
|---|---|---|---|
oven.start | — | bool | Power on |
oven.stop | — | bool | Power off |
oven.set_temperature | temperature (int, 50–250) | int | Set target temp |
oven.set_timer | seconds (int, 0–3600) | int | Set countdown |
oven.add_time | seconds (int) | int | Add time to timer |
oven.get_state | — | JSON | Read all DPs |
oven.list_recipes | — | JSON array | List preset programs |
oven.run_recipe | recipe (string) | JSON | Apply recipe + start |
device.camera_shot | — | image/jpeg | Capture a photo |
Built-in Recipes
| Recipe | Temp | Time | Best For |
|---|---|---|---|
bake | 180°C | 30 min | Cakes, bread, casseroles |
roast | 200°C | 40 min | Meat and vegetables |
broil | 230°C | 10 min | Quick browning |
pizza | 220°C | 15 min | High-heat pizza |
grill | 250°C | 8 min | Intense grilling |
reheat | 120°C | 5 min | Leftovers |
warm | 80°C | 30 min | Keep food warm |
AI Coding Prompts: Tips for Developers
When using AI coding assistants (Cursor, Claude Code, Copilot) to build hardware MCP skills, these prompt patterns will accelerate your development:
Prompt Pattern 0: Generate the Full Cloud Product from a Description
/tuya-iot-platform
Create a new AI product for a smart oven with these capabilities:
- Power on/off (bool)
- Temperature setting 50-250°C (value)
- Current temperature readback 0-300°C (value, read-only)
- Countdown timer 0-3600 seconds (value)
Add an AI Agent with device control MCP plugin.
Generate the DP definitions and the embedded DP header.
Why it works: The /tuya-iot-platform skill creates the cloud product, defines DPs, configures the AI Agent, and generates the firmware DP header — all from a natural-language device description. This is the fastest way to go from idea to code.
Prompt Pattern 1: Describe the Device, Not the Code
I have a smart oven with these features:
- Power on/off
- Temperature control (50-250°C)
- Timer (0-3600 seconds)
- Current temperature sensor
- Camera for visual inspection
Create MCP tool registrations for each feature.
Use the AI_MCP_TOOL_ADD macro pattern from the otto_robot example.
Why it works: The AI maps your device description directly to tool names, descriptions, and parameters.
Prompt Pattern 2: Specify the DP Mapping
My oven DPs are:
- DP 101: switch (bool, rw)
- DP 102: temp_set (value, 50-250, rw)
- DP 103: temp_current (value, 0-300, ro)
- DP 104: timer (value, 0-3600, rw)
Generate the tuya_dp_profile.h header and the MCP tool callbacks
that read/write these DPs.
Why it works: Explicit DP definitions eliminate ambiguity about parameter types and ranges.
Prompt Pattern 3: Request LLM-Friendly Descriptions
For each MCP tool, write descriptions that help an LLM understand:
1. WHEN to use this tool (what user intent triggers it)
2. WHAT parameters it takes (with units and ranges)
3. WHAT it returns (type and meaning)
Example: "Set the oven target temperature in Celsius (50-250).
Use when the user says 'preheat', 'set temp', or 'bake at X degrees'."
Why it works: Good tool descriptions are the #1 factor in the AI picking the right tool.
Prompt Pattern 4: Ask for Error Handling
Add input validation to each MCP tool callback:
- Clamp temperature to the DP range (50-250)
- Return -1 on invalid parameters
- For run_recipe, return {ok: false, available: [...]} on unknown recipe names
Why it works: The AI can self-correct when it gets structured error responses.
Prompt Pattern 5: Generate the Full Stack at Once
/tuyaopen-dev-loop
Create a complete Smart Oven project:
1. Cloud product with DPs (switch, temp_set, temp_current, timer)
2. Embedded firmware with mock hardware (app_oven.c)
3. MCP tools for all oven features (app_oven_mcp.c)
4. LVGL UI showing oven state on the T5-AI board display
5. Wire everything in app_chat_bot.c
Why it works: The /tuyaopen-dev-loop skill orchestrates the full cloud-to-device workflow.
Key Takeaways
- Start with the cloud product — Create the product, define DPs, and add the AI Agent on the cloud platform before touching firmware. The product is the foundation.
- DPs are the contract — Define them on the cloud, generate the header, and everything else follows.
- Tool descriptions matter — Write them for LLMs: when to use, what params, what returns.
- Defer registration to MQTT connect — Use
tal_event_subscribe(EVENT_MQTT_CONNECTED, ..., SUBSCRIBE_TYPE_ONETIME)so tools register when the cloud link is ready. - Keep hardware separate from MCP — Your
app_oven.chandles hardware;app_oven_mcp.chandles tool registration. Clean separation. - Return structured data — JSON responses with
ok: false+ available options let the AI self-correct.
Next Steps
- Create Your Product & Agent — Full guide to creating the cloud product, DPs, and AI Agent
- Custom Device MCP (Hardware Skills Guide) — Full API reference for MCP tool development
- Hardware Peripheral Skills — Built-in GPIO, ADC, I2C, UART, PWM tools
- MCP Server API — Complete MCP server documentation
- Designing Device MCP Tools — Best practices for tool design