Creating Skills
Build and deploy skills using the Dashboard, MCP server, or Developer API
This guide walks you through creating a SkillSet and deploying skills. You can use any of these three methods:
- Dashboard — visual editor with built-in code validation
- MCP Server — programmatic access from AI coding assistants
- Developer API — REST API for automation and CI/CD
A SkillSet is a container that holds one or more skills — JavaScript functions your AI agent can call during conversations. Creating a SkillSet is a two-step process:
- Create the SkillSet — define its name, description, and visibility
- Create a Version — add your skills, configuration definitions, and deploy
Each version is an immutable snapshot. Creating a new version automatically deploys it as the active version.
Plan Your Skill
Before writing code, define what your skill does:
- What problem does it solve? (e.g., look up an order status)
- What inputs does it need? (e.g., order ID)
- What should it return? (e.g., status, delivery date, tracking URL)
- Does it need configuration? (e.g., API keys, endpoint URLs)
Example: Order Status Skill
We'll build a skill that checks the status of a customer's order. It needs:
- Input: Order ID (required), Customer ID (optional)
- Output: Status message, current status, estimated delivery
- Config: API endpoint URL, API key
Write the Handler Function
Every skill requires an async function handler(input) that receives:
| Property | Description |
|---|---|
parameters | Input values extracted by the AI, matching your parameters schema |
config | Configuration values set during installation (API keys, URLs, etc.) |
context | Conversation and organization context |
Basic Example
async function handler(input) {
const { config, parameters } = input
const { orderId } = parameters
const res = await fetch(`${config.apiEndpoint}/orders/${orderId}`, {
headers: { 'Authorization': `Bearer ${config.apiKey}` }
})
if (!res.ok) {
return {
prompt: `Failed to look up order ${orderId}: ${res.statusText}`
}
}
const order = await res.json()
return {
prompt: `Order ${orderId} is currently ${order.status}. Estimated delivery: ${order.estimatedDelivery}.`,
response: {
text: `Order #${orderId}: ${order.status}`,
buttons: [
{ label: "Track Package", payload: "TRACK_PACKAGE" },
{ label: "Request Return", payload: "REQUEST_RETURN" }
]
}
}
}Rich Response Example (Carousel)
async function handler(input) {
const { config, parameters } = input
const res = await fetch(`${config.apiEndpoint}/products?q=${parameters.query}`, {
headers: { 'Authorization': `Bearer ${config.apiKey}` }
})
const { products } = await res.json()
return {
prompt: `Found ${products.length} products matching "${parameters.query}".`,
response: {
carousel: products.slice(0, 5).map(p => ({
title: p.name,
subtitle: `$${p.price}`,
imageUrl: p.imageUrl,
buttons: [
{ type: "web_url", title: "View", url: p.url },
{ type: "postback", title: "Add to Cart", payload: `ADD_${p.id}` }
]
}))
}
}
}Via Dashboard
The Dashboard provides a visual editor with built-in code validation, schema editors, and AI-powered fix suggestions.
1. Create the SkillSet
- Go to AI Settings → Skills
- Click Create New SkillSet
- Fill in the details:
- Name: Display name (1–100 characters)
- Slug: URL-friendly identifier (auto-generated from name if left blank). Lowercase letters, numbers, and underscores only, max 64 characters.
- Tagline: Short summary (max 200 characters)
- Description: What the SkillSet does (1–1000 characters)
- Visibility:
PRIVATE(your organization only) orPUBLIC(marketplace)
- Optionally upload a logo image
- Save
2. Create a Version with Skills
-
Open your SkillSet and go to the code editor
-
For each skill, fill in:
Metadata:
- Name — display name (1–100 characters)
- Slug — identifier (1–64 characters,
^[a-z0-9_]+$) - Description — what it does (1–500 characters)
- Prompt — tell the AI when and how to use this skill (1–2000 characters)
- Kind —
EXECUTE(performs an action) orSEARCH(retrieves data)
Code:
- Handler Code — your JavaScript handler function (max 50 KB)
Schemas (JSON editors):
- Parameters — JSON Schema defining the inputs the AI should extract
- Response — JSON Schema describing the handler's output structure
Optional:
- Buttons — quick reply buttons (label max 50 chars, payload max 200 chars)
-
Add Configuration Definitions if your skill needs API keys or settings (see Configuration Types)
-
The editor validates your code in real-time — fix any errors it flags
-
Enter a changelog message and publish
3. Install and Configure
- Go to AI Settings → Skills
- Find your SkillSet and click Install
- Fill in the required configuration values (API key, endpoint, etc.)
- Save
4. Connect to a Persona
- Go to AI Settings → Personas
- Select the persona
- Go to the Skills tab
- Enable the SkillSet
- Test in a conversation (try: "check the status of order ORD-12345")
Via MCP Server
The Cloodot MCP server lets you manage SkillSets from any MCP-compatible client — AI coding assistants like Claude Code, Cursor, Windsurf, or any tool that supports the Model Context Protocol.
Connecting
Server URL: https://developers.cloodot.com/mcp
Authentication: OAuth 2.0 — your MCP client handles the OAuth flow automatically. The server advertises its auth configuration via the standard /.well-known/oauth-protected-resource endpoint.
Available Tools
| Tool | Description |
|---|---|
create_skillset | Create a new SkillSet |
update_skillset | Update SkillSet metadata |
delete_skillset | Permanently delete a SkillSet |
list_skillsets | List visible SkillSets (owned + public) |
get_skillset | Get a specific SkillSet with deployed version |
create_skillset_version | Create and deploy a new version with skills |
list_skillset_versions | List all versions (newest first) |
install_skillset | Install a SkillSet into your organization |
uninstall_skillset | Remove installation and config values |
update_skillset_config | Update configuration values for an installed SkillSet |
Each tool includes full input schemas — your MCP client will present the required fields and validate inputs automatically. The same field limits from the Skill Definition Limits section apply.
Via Developer API
The Developer API is available at https://developers.cloodot.com. All endpoints require an API key sent via the x-api-key header.
For the complete API reference with request/response schemas, visit the interactive documentation at:
https://developers.cloodot.com/api/v1/reference
Quick Start
Create a SkillSet:
curl -X POST https://developers.cloodot.com/api/v1/skillsets \
-H "x-api-key: cloodot_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Order Management",
"description": "Skills for checking order status and managing shipments",
"visibility": "PRIVATE"
}'Create a version (auto-deploys):
curl -X POST https://developers.cloodot.com/api/v1/skillsets/SKILLSET_ID/versions \
-H "x-api-key: cloodot_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "changeLog": "...", "skills": [...], "configDefinitions": [...] }'Install and configure:
curl -X POST https://developers.cloodot.com/api/v1/skillsets/SKILLSET_ID/install \
-H "x-api-key: cloodot_your_key_here" \
-H "Content-Type: application/json" \
-d '{}'
curl -X PUT https://developers.cloodot.com/api/v1/skillsets/SKILLSET_ID/config \
-H "x-api-key: cloodot_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "config": { "apiEndpoint": "https://api.example.com", "apiKey": "sk-..." } }'Available Endpoints
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/skillsets | List SkillSets |
POST | /api/v1/skillsets | Create a SkillSet |
GET | /api/v1/skillsets/{id} | Get a SkillSet |
PUT | /api/v1/skillsets/{id} | Update a SkillSet |
DELETE | /api/v1/skillsets/{id} | Delete a SkillSet |
GET | /api/v1/skillsets/{id}/versions | List versions |
POST | /api/v1/skillsets/{id}/versions | Create a version (auto-deploys) |
POST | /api/v1/skillsets/{id}/install | Install a SkillSet |
POST | /api/v1/skillsets/{id}/uninstall | Uninstall a SkillSet |
PUT | /api/v1/skillsets/{id}/config | Update config values |
ActionResponse Reference
The ActionResponse is the required return format for all skill handlers. Your handler must return an object conforming to this schema.
Schema
{
prompt: string, // Required
message?: string, // Optional
data?: any, // Optional
response?: { // Optional
text?: string,
buttons?: Array<{ label: string, payload: string }>,
carousel?: Array<CarouselItem>,
imageUrl?: string,
documentUrl?: string
}
}Field Reference
| Field | Type | Required | Limits | Description |
|---|---|---|---|---|
prompt | string | Yes | No max length | Text returned to the AI as context for its reply |
message | string | No | No max length | Direct assistant message in the conversation |
data | any | No | — | Additional structured data |
response | object | No | — | Rich response content (see below) |
response.text | string | No | No max length | Text content displayed to the end user |
response.buttons | array | No | No max array length | Quick reply buttons |
response.buttons[].label | string | Yes | Max 20 characters | Button text shown to the user |
response.buttons[].payload | string | Yes | Max 20 characters | Value sent back when the button is clicked |
response.carousel | array | No | No max array length | Carousel cards |
response.imageUrl | string | No | Must be a valid HTTPS URL | Image sent as an attachment |
response.documentUrl | string | No | Must be a valid HTTPS URL | Document/file sent as an attachment |
Carousel Item Schema
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Card title |
subtitle | string | No | Card subtitle |
imageUrl | string | No | Image URL for the card |
defaultActionUrl | string | No | URL opened when the card is tapped |
buttons | array | No | Card-level action buttons |
buttons[].type | string | Yes | "web_url" or "postback" |
buttons[].title | string | Yes | Button label |
buttons[].url | string | No | URL to open (for web_url type) |
buttons[].payload | string | No | Payload sent back (for postback type) |
How Fields Are Used
prompt— Fed back to the AI model as tool output. The AI reads this to formulate a natural language response to the user. Keep it informative and concise.message— Sent as a direct assistant message, bypassing AI generation.response.text— Displayed as text content in the chat alongside the AI's response.response.buttons— Rendered as clickable quick reply buttons below the message. When clicked, thepayloadis sent back as a new message.response.carousel— Rendered as horizontally scrollable cards, each with optional image, title, subtitle, and action buttons.response.imageUrl/response.documentUrl— Sent as media attachments in the chat. Must be HTTPS URLs.
Minimal Return
return { prompt: "Order ORD-123 is shipped and arriving tomorrow." }Full Return
return {
prompt: "Found 3 products matching the search.",
response: {
text: "Here are the top results:",
buttons: [
{ label: "Show More", payload: "SHOW_MORE" }
],
carousel: [
{
title: "Widget Pro",
subtitle: "$29.99",
imageUrl: "https://example.com/widget.png",
buttons: [
{ type: "web_url", title: "View", url: "https://example.com/widget" },
{ type: "postback", title: "Buy Now", payload: "BUY_WIDGET_PRO" }
]
}
],
imageUrl: "https://example.com/promo-banner.png"
}
}Skill Definition Limits
Reference for all field limits when creating a skill version:
Skill Fields
| Field | Type | Required | Limits |
|---|---|---|---|
slug | string | Yes | 1–64 chars, pattern: ^[a-z0-9_]+$ |
name | string | Yes | 1–100 chars |
description | string | Yes | 1–500 chars |
kind | string | No | "EXECUTE" (default) or "SEARCH" |
prompt | string | Yes | 1–2000 chars |
definition | string | Yes | 1–50,000 chars (~50 KB) |
parameters | object | No | Valid JSON Schema, defaults to {} |
response | object | No | Valid JSON Schema, defaults to {} |
buttons | array | No | Array of {label, payload} |
buttons[].label | string | Yes | 1–50 chars |
buttons[].payload | string | Yes | 1–200 chars |
SkillSet Fields
| Field | Type | Required | Limits |
|---|---|---|---|
name | string | Yes | 1–100 chars |
slug | string | No | 1–64 chars, pattern: ^[a-z0-9_]+$, auto-generated if omitted |
tagline | string | No | Max 200 chars |
description | string | Yes | 1–1000 chars |
visibility | string | No | "PUBLIC" or "PRIVATE" (default) |
logoImageUrl | string | No | Valid URL |
bannerImageUrl | string | No | Valid URL |
Version Fields
| Field | Type | Required | Limits |
|---|---|---|---|
changeLog | string | Yes | 1–1000 chars |
skills | array | Yes | At least 1 skill |
Configuration Types
| Field | Type | Required | Limits |
|---|---|---|---|
key | string | Yes | 1–50 chars (camelCase recommended) |
label | string | Yes | 1–100 chars |
type | string | Yes | STRING, NUMBER, BOOLEAN, SELECT, MULTI_SELECT, SECRET |
description | string | No | Max 500 chars |
required | boolean | No | Defaults to false |
defaultValue | string | No | — |
order | number | No | Integer, defaults to 0 |
options | array | No | Array of strings (for SELECT/MULTI_SELECT) |
validation | string | No | Max 200 chars, must be a valid regular expression |
isSensitive | boolean | No | Defaults to false. Encrypts and masks the value in UI. |
Troubleshooting
Handler Not Found
Error: "handler function not found"
Your skill code must define an async handler function:
async function handler(input) {
// ...
}Configuration Not Available
Error: config.apiKey is undefined
- Ensure the configuration key is defined in
configDefinitions - Verify the SkillSet is installed and configured with values
- Check that the
keyin your config definition matches what you access in code
Skill Not Appearing in Conversations
- Confirm the SkillSet version is deployed (creating a version auto-deploys)
- Check the SkillSet is installed in your organization
- Verify the persona has the SkillSet enabled
- Make sure the
promptfield clearly describes when the AI should use the skill
Invalid Response
If your handler returns a value that doesn't match the ActionResponse format (e.g., missing the prompt field), the execution will fail. Always return at least { prompt: "..." }.
Next Steps
- Skill Schema Reference — Full schema specification and validation rules
- Skill Sets — Organize and manage SkillSet collections
- AI Agent Configuration — Connect skills to your AI agent