Skills Format Specification
The skills.md format defines how Skills are structured, configured, and executed. This specification ensures consistent behavior across all Tajo Skills.
File Structure
A skill is defined in a single markdown file with YAML frontmatter:
---name: customer-syncversion: 2.1.0description: Sync customer data to Brevo contactscategory: data-syncstatus: stable
triggers: - event: customer_created - event: customer_updated - event: customer_deleted
actions: - brevo/create-contact - brevo/update-contact - brevo/delete-contact
brevoEndpoints: - POST /v3/contacts - PUT /v3/contacts/{identifier} - DELETE /v3/contacts/{identifier}
permissions: - contacts:write - contacts:read---
# Customer Sync
Automatically synchronize customer data from your platform to Brevo contacts.
## Overview
This skill listens for customer lifecycle events and mirrors changes to Brevo...Frontmatter Fields
Required Fields
| Field | Type | Description |
|---|---|---|
name | string | Unique identifier (kebab-case) |
version | string | Semantic version (e.g., “2.1.0”) |
description | string | Short description (max 160 chars) |
category | enum | One of: data-sync, email-marketing, sms-whatsapp, loyalty, analytics, integrations |
triggers | array | Events that activate this skill |
actions | array | Operations this skill can perform |
Optional Fields
| Field | Type | Default | Description |
|---|---|---|---|
status | enum | stable | One of: stable, beta, experimental |
brevoEndpoints | array | [] | Brevo API endpoints used |
permissions | array | [] | Required Brevo API permissions |
relatedSkills | array | [] | IDs of related skills |
featured | boolean | false | Highlight in skill catalog |
Triggers
Triggers define what events activate the skill.
Event Triggers
triggers: - event: cart_abandoned conditions: - cart_value: "> 50" # Minimum cart value - items_count: ">= 1" # At least one item - time_since_activity: "> 30m" # 30 minutes of inactivity debounce: 5m # Wait 5 minutes before re-triggeringSchedule Triggers
triggers: - schedule: "0 9 * * *" # Cron expression (daily at 9am) timezone: "America/New_York" - schedule: every_hour - schedule: every_dayWebhook Triggers
triggers: - webhook: /skills/customer-sync/trigger method: POST authentication: api_keyCondition Operators
| Operator | Description | Example |
|---|---|---|
= | Equals | status: "active" |
!= | Not equals | status: "!= deleted" |
> | Greater than | cart_value: "> 50" |
>= | Greater than or equal | items: ">= 1" |
< | Less than | age: "< 30" |
<= | Less than or equal | quantity: "<= 10" |
contains | String contains | email: "contains @gmail.com" |
starts_with | String prefix | name: "starts_with Dr." |
in | Value in list | country: "in US,CA,UK" |
Actions
Actions define what operations the skill performs.
Action Definition
actions: - id: send_reminder_email type: brevo/send-email parameters: template_id: 12345 to: "{{ contact.email }}" params: first_name: "{{ contact.firstName }}" cart_items: "{{ cart.items }}" retry: attempts: 3 backoff: exponential on_error: continue # or 'stop'Action Types
| Type | Description |
|---|---|
brevo/send-email | Send transactional email |
brevo/send-sms | Send SMS message |
brevo/send-whatsapp | Send WhatsApp message |
brevo/create-contact | Create new contact |
brevo/update-contact | Update contact attributes |
brevo/delete-contact | Delete contact |
brevo/add-to-list | Add contact to list |
brevo/remove-from-list | Remove contact from list |
brevo/track-event | Track custom event |
http/request | Make HTTP request |
transform/map | Transform data |
control/delay | Wait before continuing |
control/condition | Branch based on condition |
Template Variables
Use {{ }} syntax to reference data:
parameters: to: "{{ contact.email }}" subject: "Your order #{{ order.number }} has shipped" params: name: "{{ contact.firstName | default: 'Customer' }}" items: "{{ cart.items | map: 'name' | join: ', ' }}" total: "{{ cart.total | currency: 'USD' }}"Available Filters
| Filter | Description | Example |
|---|---|---|
default | Default value | {{ name | default: 'Guest' }} |
uppercase | Uppercase string | {{ name | uppercase }} |
lowercase | Lowercase string | {{ email | lowercase }} |
capitalize | Capitalize first letter | {{ name | capitalize }} |
truncate | Truncate string | {{ desc | truncate: 100 }} |
date | Format date | {{ date | date: 'YYYY-MM-DD' }} |
currency | Format currency | {{ price | currency: 'EUR' }} |
map | Map array property | {{ items | map: 'name' }} |
join | Join array | {{ tags | join: ', ' }} |
first | First array item | {{ items | first }} |
last | Last array item | {{ items | last }} |
size | Array/string length | {{ items | size }} |
Permissions
Define required Brevo API permissions:
permissions: - contacts:read # Read contact data - contacts:write # Create/update contacts - email:send # Send transactional emails - sms:send # Send SMS messages - lists:write # Manage contact lists - events:write # Track eventsComplete Example
---name: abandoned-cart-recoveryversion: 3.0.0description: Recover abandoned shopping carts with a multi-step email sequencecategory: email-marketingstatus: stable
triggers: - event: cart_abandoned conditions: - cart_value: "> 25" - customer_email: "exists" - items_count: ">= 1" debounce: 30m
actions: - id: wait_1h type: control/delay parameters: duration: 1h
- id: check_purchase type: control/condition parameters: condition: "{{ cart.converted }} = false" then: send_first_email else: stop
- id: send_first_email type: brevo/send-email parameters: template_id: {{ env.ABANDONED_CART_TEMPLATE_1 }} to: "{{ contact.email }}" params: first_name: "{{ contact.firstName }}" cart_url: "{{ cart.recovery_url }}" items: "{{ cart.items }}" total: "{{ cart.total | currency }}"
- id: wait_24h type: control/delay parameters: duration: 24h
- id: check_purchase_2 type: control/condition parameters: condition: "{{ cart.converted }} = false" then: send_second_email else: stop
- id: send_second_email type: brevo/send-email parameters: template_id: {{ env.ABANDONED_CART_TEMPLATE_2 }} to: "{{ contact.email }}" params: first_name: "{{ contact.firstName }}" cart_url: "{{ cart.recovery_url }}" discount_code: "{{ generate_discount(10, 'percent') }}"
brevoEndpoints: - POST /v3/smtp/email - GET /v3/contacts/{identifier}
permissions: - contacts:read - email:send
relatedSkills: - customer-sync - order-events - browse-abandonment---
# Abandoned Cart Recovery
Recover lost sales with a proven multi-step email sequence...MCP Tool Exposure
Skills can be exposed as MCP tools, making them callable by AI agents. Add the mcp field to your frontmatter:
---name: abandoned-cart-recoveryversion: 3.0.0description: Recover abandoned shopping carts with a multi-step email sequencecategory: email-marketing
# MCP Configurationmcp: tool_name: tajo/recover-abandoned-cart description: Execute abandoned cart recovery sequence for a specific cart inputSchema: type: object properties: cart_id: type: string description: The abandoned cart identifier customer_email: type: string description: Customer email address min_cart_value: type: number description: Minimum cart value to trigger recovery default: 25 required: [cart_id, customer_email] brevo_servers: - brevo_contacts - brevo_email_campaign_management - brevo_templates---How MCP Exposure Works
When a skill has an mcp field, it becomes a tool that AI agents can discover and invoke:
Agent: "Recover abandoned cart #4521 for [email protected]" ↓MCP Tool Discovery: finds tajo/recover-abandoned-cart ↓Tool Execution: runs the skill's action chain ↓Brevo MCP Servers: contacts + email modules called ↓Result: recovery sequence initiatedMCP Fields
| Field | Type | Description |
|---|---|---|
mcp.tool_name | string | MCP tool name (format: tajo/skill-name) |
mcp.description | string | Description shown to AI agents during tool discovery |
mcp.inputSchema | object | JSON Schema defining the tool’s input parameters |
mcp.brevo_servers | array | Which Brevo MCP server modules this skill needs |
Skills vs Direct MCP Tools
| Aspect | Skill as MCP Tool | Direct Brevo MCP |
|---|---|---|
| Abstraction | High, agent says “recover this cart” | Low, agent must call individual Brevo endpoints |
| Complexity | Encapsulates multi-step logic | Agent must orchestrate each step |
| Guardrails | Built into the skill (conditions, debounce) | Agent must implement its own |
| Best for | Repeatable workflows | Ad-hoc operations |
Tip
Use Skills as MCP tools for complex, multi-step workflows that agents shouldn’t reinvent each time. Use direct Brevo MCP tools for simple, one-off operations like looking up a contact or sending a single email.
Composing Skills and Agents
An agent can use both Skills (as MCP tools) and direct Brevo MCP modules:
# Agent spec---name: retention-agenttools: # Tajo Skills as MCP tools - tajo/recover-abandoned-cart - tajo/customer-sync - tajo/win-back-sequence # Direct Brevo MCP modules - brevo_contacts - brevo_campaign_analytics - brevo_segments---This gives agents high-level automation (Skills) for common patterns while retaining low-level access (Brevo MCP) for custom logic.
See Agent Specification Format and Building Agents for full details.
Best Practices
Tip
Version your skills using semantic versioning. Breaking changes require a major version bump.
- Keep skills focused - One skill should do one thing well
- Use descriptive names -
abandoned-cart-recoverynotacr - Document conditions - Explain why each condition exists
- Handle errors gracefully - Use
on_error: continuefor non-critical actions - Test with small audiences - Use
status: experimentalduring development - Add MCP exposure for skills that agents should be able to invoke directly
Next Steps
- MCP & Agents Overview - How Skills fit into the agentic architecture
- Agent Specification Format - Define agents that use Skills as tools
- Building Agents - Build your first marketing agent
- Data Sync Skills - Explore data synchronization skills