Skip to content

Commit c0f2017

Browse files
feat(ai): add metadata management tools and skill (Tool→Skill→Agent)
Add 6 platform built-in AI tools for metadata operations: - create_object: Create a new data object/table - add_field: Add a field/column to an existing object - modify_field: Modify an existing field definition - delete_field: Remove a field from an object - list_objects: List all registered objects/tables - describe_object: Get full schema details of an object Aggregate into metadata_management skill with trigger phrases, programmatic activation conditions, and permission controls. Includes 26 unit tests, CHANGELOG.md and ROADMAP.md updates. Agent-Logs-Url: https://github.com/objectstack-ai/spec/sessions/43586d7e-772c-4365-8c16-9c53831d4397 Co-authored-by: xuyushun441-sys <255036401+xuyushun441-sys@users.noreply.github.com>
1 parent 99e571d commit c0f2017

7 files changed

Lines changed: 757 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
absent, fixing the CI `@objectstack/client#test` failure.
1616

1717
### Added
18+
- **Metadata Management Tools & Skill (Tool→Skill→Agent)** — Added 6 platform built-in AI
19+
tools for metadata operations (`create_object`, `add_field`, `modify_field`, `delete_field`,
20+
`list_objects`, `describe_object`) in `packages/spec/src/ai/metadata-tools.zod.ts`. Each
21+
tool has structured JSON Schema parameters, output schema, permission controls, and
22+
confirmation requirements for destructive operations. Aggregated into `metadata_management`
23+
skill in `metadata-skill.zod.ts` with trigger phrases and programmatic activation conditions.
24+
Fully testable with 26 unit tests covering individual tool definitions, schema validation,
25+
aggregate exports, and skill structure.
1826
- **Agent Skills — `skills/` directory (agentskills.io)** — Created `skills/` folder at
1927
repository root following the [agentskills.io specification](https://agentskills.io/specification).
2028
Five expert-knowledge skills with hand-written `SKILL.md` files and `references/` quick-lookup

ROADMAP.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,7 @@ Objects now declare `namespace: 'sys'` and a short `name` (e.g., `name: 'user'`)
663663
- [ ] Agent runtime — execute AI agents defined in spec schemas
664664
- [x] Tool registry — connect agents to ObjectQL operations, APIs, and workflows (initial implementation in `service-ai`)
665665
- [x] Conversation management — persistent chat with context windows (initial implementation in `service-ai`)
666+
- [x] Metadata management tools — 6 built-in tools (`create_object`, `add_field`, `modify_field`, `delete_field`, `list_objects`, `describe_object`) with structured parameters, permissions, and `metadata_management` skill
666667

667668
### 7.3 RAG Pipeline
668669

packages/spec/src/ai/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
export * from './agent.zod';
2424
export * from './tool.zod';
2525
export * from './skill.zod';
26+
export * from './metadata-tools.zod';
27+
export * from './metadata-skill.zod';
2628
export * from './agent-action.zod';
2729
export * from './devops-agent.zod';
2830
export * from './plugin-development.zod';
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { describe, it, expect } from 'vitest';
2+
import { metadataManagementSkill } from './metadata-skill.zod';
3+
import { SkillSchema } from './skill.zod';
4+
import { METADATA_TOOL_NAMES } from './metadata-tools.zod';
5+
6+
describe('metadataManagementSkill', () => {
7+
it('should pass SkillSchema validation', () => {
8+
expect(() => SkillSchema.parse(metadataManagementSkill)).not.toThrow();
9+
});
10+
11+
it('should have correct identity', () => {
12+
expect(metadataManagementSkill.name).toBe('metadata_management');
13+
expect(metadataManagementSkill.label).toBe('Metadata Management');
14+
expect(metadataManagementSkill.active).toBe(true);
15+
});
16+
17+
it('should have description and instructions', () => {
18+
expect(metadataManagementSkill.description).toBeDefined();
19+
expect(metadataManagementSkill.description!.length).toBeGreaterThan(20);
20+
expect(metadataManagementSkill.instructions).toBeDefined();
21+
expect(metadataManagementSkill.instructions!.length).toBeGreaterThan(20);
22+
});
23+
24+
it('should reference all metadata tool names', () => {
25+
expect(metadataManagementSkill.tools).toHaveLength(METADATA_TOOL_NAMES.length);
26+
METADATA_TOOL_NAMES.forEach(toolName => {
27+
expect(metadataManagementSkill.tools).toContain(toolName);
28+
});
29+
});
30+
31+
it('should have trigger phrases for intent matching', () => {
32+
expect(metadataManagementSkill.triggerPhrases).toBeDefined();
33+
expect(metadataManagementSkill.triggerPhrases!.length).toBeGreaterThan(5);
34+
35+
// Should cover core operations
36+
const phrases = metadataManagementSkill.triggerPhrases!.join(' ');
37+
expect(phrases).toContain('create');
38+
expect(phrases).toContain('add');
39+
expect(phrases).toContain('delete');
40+
expect(phrases).toContain('list');
41+
expect(phrases).toContain('describe');
42+
});
43+
44+
it('should have trigger conditions for programmatic activation', () => {
45+
expect(metadataManagementSkill.triggerConditions).toBeDefined();
46+
expect(metadataManagementSkill.triggerConditions!.length).toBeGreaterThan(0);
47+
48+
const intentCondition = metadataManagementSkill.triggerConditions!.find(c => c.field === 'intent');
49+
expect(intentCondition).toBeDefined();
50+
expect(intentCondition!.operator).toBe('in');
51+
expect(intentCondition!.value).toContain('metadata_management');
52+
});
53+
54+
it('should have permissions covering all CRUD operations', () => {
55+
expect(metadataManagementSkill.permissions).toBeDefined();
56+
const perms = metadataManagementSkill.permissions!;
57+
expect(perms).toContain('metadata.object.read');
58+
expect(perms).toContain('metadata.object.create');
59+
expect(perms).toContain('metadata.field.create');
60+
expect(perms).toContain('metadata.field.update');
61+
expect(perms).toContain('metadata.field.delete');
62+
});
63+
64+
it('should follow snake_case for name', () => {
65+
expect(metadataManagementSkill.name).toMatch(/^[a-z_][a-z0-9_]*$/);
66+
});
67+
68+
it('tool references should all follow snake_case', () => {
69+
metadataManagementSkill.tools.forEach(toolName => {
70+
expect(toolName).toMatch(/^[a-z_][a-z0-9_]*$/);
71+
});
72+
});
73+
});
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
import { defineSkill, type Skill } from './skill.zod';
4+
import { METADATA_TOOL_NAMES } from './metadata-tools.zod';
5+
6+
// ==========================================
7+
// Metadata Management Skill
8+
// ==========================================
9+
10+
/**
11+
* Metadata Management Skill
12+
*
13+
* Aggregates all metadata CRUD tools into a single capability bundle
14+
* that can be attached to any Agent via the Agent → Skill → Tool architecture.
15+
*
16+
* This skill enables AI agents to create, modify, and inspect
17+
* data objects and fields through structured, permission-controlled operations.
18+
*
19+
* @example
20+
* ```ts
21+
* const agent = defineAgent({
22+
* name: 'metadata_assistant',
23+
* label: 'Metadata Assistant',
24+
* role: 'Data Architect',
25+
* instructions: 'Help users design and manage their data models.',
26+
* skills: ['metadata_management'],
27+
* });
28+
* ```
29+
*/
30+
export const metadataManagementSkill: Skill = defineSkill({
31+
name: 'metadata_management',
32+
label: 'Metadata Management',
33+
description:
34+
'Provides AI-driven metadata operations including creating objects/tables, ' +
35+
'adding, modifying, and deleting fields, and inspecting schema definitions. ' +
36+
'Enables agents to serve as data architects and schema designers.',
37+
instructions:
38+
'You have access to metadata management tools that can create and modify data objects (tables) and their fields (columns). ' +
39+
'Always use `list_objects` or `describe_object` to understand the current schema before making changes. ' +
40+
'Use `create_object` to create new tables, `add_field` to add columns, `modify_field` to change existing columns, ' +
41+
'and `delete_field` to remove columns. Destructive operations (modify_field type changes, delete_field) require explicit user confirmation. ' +
42+
'Follow snake_case naming conventions for all machine names.',
43+
tools: [...METADATA_TOOL_NAMES],
44+
triggerPhrases: [
45+
'create a table',
46+
'create an object',
47+
'add a field',
48+
'add a column',
49+
'modify a field',
50+
'change a column',
51+
'delete a field',
52+
'remove a column',
53+
'list all tables',
54+
'list all objects',
55+
'describe a table',
56+
'show table schema',
57+
'show object fields',
58+
'design a data model',
59+
],
60+
triggerConditions: [
61+
{ field: 'intent', operator: 'in', value: ['schema_design', 'metadata_management', 'data_modeling'] },
62+
],
63+
permissions: ['metadata.object.read', 'metadata.object.create', 'metadata.field.create', 'metadata.field.update', 'metadata.field.delete'],
64+
});
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import { describe, it, expect } from 'vitest';
2+
import {
3+
createObjectTool,
4+
addFieldTool,
5+
modifyFieldTool,
6+
deleteFieldTool,
7+
listObjectsTool,
8+
describeObjectTool,
9+
METADATA_TOOLS,
10+
METADATA_TOOL_NAMES,
11+
} from './metadata-tools.zod';
12+
import { ToolSchema } from './tool.zod';
13+
14+
describe('Metadata Tools — individual definitions', () => {
15+
it('create_object should have correct structure', () => {
16+
expect(createObjectTool.name).toBe('create_object');
17+
expect(createObjectTool.label).toBe('Create Object');
18+
expect(createObjectTool.category).toBe('action');
19+
expect(createObjectTool.builtIn).toBe(true);
20+
expect(createObjectTool.requiresConfirmation).toBe(true);
21+
expect(createObjectTool.permissions).toContain('metadata.object.create');
22+
expect(createObjectTool.parameters).toHaveProperty('required');
23+
expect((createObjectTool.parameters as Record<string, unknown>).required).toContain('name');
24+
expect((createObjectTool.parameters as Record<string, unknown>).required).toContain('label');
25+
});
26+
27+
it('add_field should have correct structure', () => {
28+
expect(addFieldTool.name).toBe('add_field');
29+
expect(addFieldTool.label).toBe('Add Field');
30+
expect(addFieldTool.category).toBe('action');
31+
expect(addFieldTool.builtIn).toBe(true);
32+
expect(addFieldTool.requiresConfirmation).toBe(true);
33+
expect(addFieldTool.permissions).toContain('metadata.field.create');
34+
expect((addFieldTool.parameters as Record<string, unknown>).required).toContain('objectName');
35+
expect((addFieldTool.parameters as Record<string, unknown>).required).toContain('name');
36+
expect((addFieldTool.parameters as Record<string, unknown>).required).toContain('type');
37+
});
38+
39+
it('modify_field should have correct structure', () => {
40+
expect(modifyFieldTool.name).toBe('modify_field');
41+
expect(modifyFieldTool.label).toBe('Modify Field');
42+
expect(modifyFieldTool.category).toBe('action');
43+
expect(modifyFieldTool.builtIn).toBe(true);
44+
expect(modifyFieldTool.requiresConfirmation).toBe(true);
45+
expect(modifyFieldTool.permissions).toContain('metadata.field.update');
46+
expect((modifyFieldTool.parameters as Record<string, unknown>).required).toContain('objectName');
47+
expect((modifyFieldTool.parameters as Record<string, unknown>).required).toContain('fieldName');
48+
expect((modifyFieldTool.parameters as Record<string, unknown>).required).toContain('changes');
49+
});
50+
51+
it('delete_field should have correct structure', () => {
52+
expect(deleteFieldTool.name).toBe('delete_field');
53+
expect(deleteFieldTool.label).toBe('Delete Field');
54+
expect(deleteFieldTool.category).toBe('action');
55+
expect(deleteFieldTool.builtIn).toBe(true);
56+
expect(deleteFieldTool.requiresConfirmation).toBe(true);
57+
expect(deleteFieldTool.permissions).toContain('metadata.field.delete');
58+
expect((deleteFieldTool.parameters as Record<string, unknown>).required).toContain('objectName');
59+
expect((deleteFieldTool.parameters as Record<string, unknown>).required).toContain('fieldName');
60+
});
61+
62+
it('list_objects should have correct structure', () => {
63+
expect(listObjectsTool.name).toBe('list_objects');
64+
expect(listObjectsTool.label).toBe('List Objects');
65+
expect(listObjectsTool.category).toBe('data');
66+
expect(listObjectsTool.builtIn).toBe(true);
67+
expect(listObjectsTool.requiresConfirmation).toBe(false);
68+
expect(listObjectsTool.permissions).toContain('metadata.object.read');
69+
});
70+
71+
it('describe_object should have correct structure', () => {
72+
expect(describeObjectTool.name).toBe('describe_object');
73+
expect(describeObjectTool.label).toBe('Describe Object');
74+
expect(describeObjectTool.category).toBe('data');
75+
expect(describeObjectTool.builtIn).toBe(true);
76+
expect(describeObjectTool.requiresConfirmation).toBe(false);
77+
expect(describeObjectTool.permissions).toContain('metadata.object.read');
78+
expect((describeObjectTool.parameters as Record<string, unknown>).required).toContain('objectName');
79+
});
80+
});
81+
82+
describe('Metadata Tools — schema validation', () => {
83+
it('every tool should pass ToolSchema validation', () => {
84+
METADATA_TOOLS.forEach(tool => {
85+
expect(() => ToolSchema.parse(tool)).not.toThrow();
86+
});
87+
});
88+
89+
it('every tool should have a description for LLM consumption', () => {
90+
METADATA_TOOLS.forEach(tool => {
91+
expect(tool.description.length).toBeGreaterThan(20);
92+
});
93+
});
94+
95+
it('every tool should have parameters as a JSON Schema object', () => {
96+
METADATA_TOOLS.forEach(tool => {
97+
expect(tool.parameters).toHaveProperty('type', 'object');
98+
expect(tool.parameters).toHaveProperty('properties');
99+
});
100+
});
101+
102+
it('every tool should have an outputSchema', () => {
103+
METADATA_TOOLS.forEach(tool => {
104+
expect(tool.outputSchema).toBeDefined();
105+
expect(tool.outputSchema).toHaveProperty('type', 'object');
106+
});
107+
});
108+
109+
it('every tool should have permissions defined', () => {
110+
METADATA_TOOLS.forEach(tool => {
111+
expect(tool.permissions).toBeDefined();
112+
expect(tool.permissions!.length).toBeGreaterThan(0);
113+
});
114+
});
115+
116+
it('every tool should be marked as builtIn', () => {
117+
METADATA_TOOLS.forEach(tool => {
118+
expect(tool.builtIn).toBe(true);
119+
});
120+
});
121+
122+
it('write tools should require confirmation, read tools should not', () => {
123+
const writeTools = [createObjectTool, addFieldTool, modifyFieldTool, deleteFieldTool];
124+
const readTools = [listObjectsTool, describeObjectTool];
125+
126+
writeTools.forEach(tool => {
127+
expect(tool.requiresConfirmation).toBe(true);
128+
});
129+
readTools.forEach(tool => {
130+
expect(tool.requiresConfirmation).toBe(false);
131+
});
132+
});
133+
});
134+
135+
describe('METADATA_TOOLS aggregate', () => {
136+
it('should contain exactly 6 tools', () => {
137+
expect(METADATA_TOOLS).toHaveLength(6);
138+
});
139+
140+
it('should match METADATA_TOOL_NAMES ordering', () => {
141+
METADATA_TOOLS.forEach((tool, index) => {
142+
expect(tool.name).toBe(METADATA_TOOL_NAMES[index]);
143+
});
144+
});
145+
146+
it('METADATA_TOOL_NAMES should contain all expected names', () => {
147+
expect(METADATA_TOOL_NAMES).toEqual([
148+
'create_object',
149+
'add_field',
150+
'modify_field',
151+
'delete_field',
152+
'list_objects',
153+
'describe_object',
154+
]);
155+
});
156+
157+
it('every tool name should follow snake_case convention', () => {
158+
METADATA_TOOL_NAMES.forEach(name => {
159+
expect(name).toMatch(/^[a-z_][a-z0-9_]*$/);
160+
});
161+
});
162+
});

0 commit comments

Comments
 (0)