- Added a new section in the MCP documentation detailing the directory structure for assistants, enhancing clarity on how to define namespaced MCP servers. - Included examples of using assistant models within MCP tools, providing guidance on referencing models in tool configurations. - Updated the README to include a link to the new Models documentation, improving navigation and resource accessibility for users.
9.1 KiB
9.1 KiB
Assistant Models
Assistants can define their own namespaced data models in the models/ directory. These models are automatically loaded with the agents.<assistant-id>. prefix and use isolated database tables.
Directory Structure
assistants/
└── my-assistant/
├── package.yao
└── models/
├── order.mod.yao # → agents.my-assistant.order
├── item.mod.yao # → agents.my-assistant.item
└── nested/
└── log.mod.yao # → agents.my-assistant.nested.log
Model Definition
Standard Yao model definition with automatic table prefixing.
models/order.mod.yao
{
"name": "Order",
"label": "Order Record",
"description": "Customer orders",
"table": {
"name": "order",
"comment": "Order records"
},
"columns": [
{
"name": "id",
"type": "ID",
"label": "ID",
"primary": true
},
{
"name": "order_no",
"type": "string",
"label": "Order Number",
"length": 100,
"nullable": false,
"unique": true,
"index": true
},
{
"name": "customer_id",
"type": "string",
"label": "Customer ID",
"length": 255,
"nullable": false,
"index": true
},
{
"name": "total_amount",
"type": "decimal",
"label": "Total Amount",
"precision": 15,
"scale": 2,
"nullable": false
},
{
"name": "status",
"type": "enum",
"label": "Status",
"option": ["pending", "confirmed", "shipped", "completed", "cancelled"],
"default": "pending",
"nullable": false,
"index": true
},
{
"name": "metadata",
"type": "json",
"label": "Metadata",
"nullable": true
}
],
"relations": {
"items": {
"type": "hasMany",
"model": "item",
"key": "order_id",
"foreign": "id"
}
},
"indexes": [
{
"name": "idx_customer_status",
"columns": ["customer_id", "status"],
"type": "index"
}
],
"option": {
"timestamps": true,
"soft_deletes": true
}
}
Table Naming
Tables are automatically prefixed with agents_<assistant-id>_:
| Assistant ID | Model File | Model ID | Table Name |
|---|---|---|---|
expense |
models/order.mod.yao |
agents.expense.order |
agents_expense_order |
tests.demo |
models/user.mod.yao |
agents.tests.demo.user |
agents_tests_demo_user |
Using Models
In Hooks
import { Process } from "@yao/runtime";
function Create(ctx: agent.Context, messages: agent.Message[]): agent.Create {
// Query assistant's own model
const orders = Process("models.agents.my-assistant.order.Paginate", {
wheres: [{ column: "status", value: "pending" }],
limit: 10,
});
return { messages };
}
In MCP Tools
{
"transport": "process",
"tools": {
"list_orders": "models.agents.my-assistant.order.Paginate",
"get_order": "models.agents.my-assistant.order.Find",
"create_order": "models.agents.my-assistant.order.Create",
"update_order": "models.agents.my-assistant.order.Update"
}
}
In Scripts
src/orders.ts
import { Process } from "@yao/runtime";
export function ListPending(): any[] {
return Process("models.agents.my-assistant.order.Get", {
wheres: [{ column: "status", value: "pending" }],
orders: [{ column: "created_at", option: "desc" }],
});
}
export function CreateOrder(data: any): any {
return Process("models.agents.my-assistant.order.Create", data);
}
export function UpdateStatus(id: number, status: string): any {
return Process("models.agents.my-assistant.order.Update", id, { status });
}
Column Types
| Type | Description | Options |
|---|---|---|
ID |
Auto-increment primary key | primary: true |
string |
VARCHAR | length (default: 255) |
text |
TEXT | - |
integer |
INT | - |
bigInteger |
BIGINT | - |
float |
FLOAT | precision, scale |
decimal |
DECIMAL | precision, scale |
boolean |
BOOLEAN | - |
date |
DATE | - |
datetime |
DATETIME | - |
timestamp |
TIMESTAMP | - |
json |
JSON/JSONB | - |
enum |
ENUM | option: [...] |
Column Options
| Option | Type | Description |
|---|---|---|
nullable |
boolean |
Allow NULL values |
default |
any |
Default value |
unique |
boolean |
Unique constraint |
index |
boolean |
Create index |
primary |
boolean |
Primary key |
length |
integer |
String length |
precision |
integer |
Decimal precision |
scale |
integer |
Decimal scale |
comment |
string |
Column comment |
Relations
{
"relations": {
"items": {
"type": "hasMany",
"model": "item",
"key": "order_id",
"foreign": "id"
},
"customer": {
"type": "hasOne",
"model": "customer",
"key": "id",
"foreign": "customer_id"
}
}
}
| Type | Description |
|---|---|
hasOne |
One-to-one relationship |
hasMany |
One-to-many relationship |
hasOneThrough |
Has one through intermediate |
hasManyThrough |
Has many through intermediate |
Model Options
{
"option": {
"timestamps": true,
"soft_deletes": true,
"permission": true
}
}
| Option | Description |
|---|---|
timestamps |
Add created_at, updated_at columns |
soft_deletes |
Add deleted_at for soft delete |
permission |
Enable permission checks |
Process Reference
Common model processes:
| Process | Description | Arguments |
|---|---|---|
Find |
Get by ID | id, query? |
Get |
Get records | query |
Paginate |
Paginated list | query, page, pagesize |
Create |
Create record | data |
Update |
Update record | id, data |
Save |
Create or update | data |
Delete |
Delete record | id |
Destroy |
Hard delete | id |
Insert |
Batch insert | columns, rows |
UpdateWhere |
Batch update | query, data |
DeleteWhere |
Batch delete | query |
Migration
Models are automatically migrated when Yao starts. The migration:
- Creates tables if not exist
- Adds new columns
- Creates indexes
- Does NOT drop columns (safe migration)
To force schema sync:
yao migrate --reset # Warning: drops and recreates tables
Example: Complete Assistant with Models
assistants/inventory/package.yao
{
"name": "Inventory Assistant",
"connector": "gpt-4o",
"mcp": {
"servers": [{ "server_id": "inventory" }]
}
}
assistants/inventory/models/product.mod.yao
{
"name": "Product",
"table": { "name": "product" },
"columns": [
{ "name": "id", "type": "ID", "primary": true },
{ "name": "sku", "type": "string", "length": 50, "unique": true },
{ "name": "name", "type": "string", "length": 200 },
{ "name": "quantity", "type": "integer", "default": 0 },
{ "name": "price", "type": "decimal", "precision": 10, "scale": 2 }
],
"option": { "timestamps": true }
}
assistants/inventory/mcps/inventory.mcp.yao
{
"label": "Inventory",
"transport": "process",
"tools": {
"list_products": "models.agents.inventory.product.Paginate",
"get_product": "models.agents.inventory.product.Find",
"update_stock": "agents.inventory.stock.Update"
}
}
assistants/inventory/src/stock.ts
import { Process } from "@yao/runtime";
export function Update(args: { sku: string; quantity: number }): any {
const product = Process("models.agents.inventory.product.Get", {
wheres: [{ column: "sku", value: args.sku }],
limit: 1,
});
if (!product || product.length === 0) {
throw new Error(`Product not found: ${args.sku}`);
}
return Process("models.agents.inventory.product.Update", product[0].id, {
quantity: args.quantity,
});
}