ag-cortex 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/commands/test-browser.md +339 -0
- package/.agent/rules/00-constitution.md +46 -0
- package/.agent/rules/project-rules.md +49 -0
- package/.agent/skills/agent-browser/SKILL.md +223 -0
- package/.agent/skills/agent-native-architecture/SKILL.md +435 -0
- package/.agent/skills/agent-native-architecture/references/action-parity-discipline.md +409 -0
- package/.agent/skills/agent-native-architecture/references/agent-execution-patterns.md +467 -0
- package/.agent/skills/agent-native-architecture/references/agent-native-testing.md +582 -0
- package/.agent/skills/agent-native-architecture/references/architecture-patterns.md +478 -0
- package/.agent/skills/agent-native-architecture/references/dynamic-context-injection.md +338 -0
- package/.agent/skills/agent-native-architecture/references/files-universal-interface.md +301 -0
- package/.agent/skills/agent-native-architecture/references/from-primitives-to-domain-tools.md +359 -0
- package/.agent/skills/agent-native-architecture/references/mcp-tool-design.md +506 -0
- package/.agent/skills/agent-native-architecture/references/mobile-patterns.md +871 -0
- package/.agent/skills/agent-native-architecture/references/product-implications.md +443 -0
- package/.agent/skills/agent-native-architecture/references/refactoring-to-prompt-native.md +317 -0
- package/.agent/skills/agent-native-architecture/references/self-modification.md +269 -0
- package/.agent/skills/agent-native-architecture/references/shared-workspace-architecture.md +680 -0
- package/.agent/skills/agent-native-architecture/references/system-prompt-design.md +250 -0
- package/.agent/skills/agent-native-reviewer/SKILL.md +246 -0
- package/.agent/skills/andrew-kane-gem-writer/SKILL.md +184 -0
- package/.agent/skills/andrew-kane-gem-writer/references/database-adapters.md +231 -0
- package/.agent/skills/andrew-kane-gem-writer/references/module-organization.md +121 -0
- package/.agent/skills/andrew-kane-gem-writer/references/rails-integration.md +183 -0
- package/.agent/skills/andrew-kane-gem-writer/references/resources.md +119 -0
- package/.agent/skills/andrew-kane-gem-writer/references/testing-patterns.md +261 -0
- package/.agent/skills/ankane-readme-writer/SKILL.md +50 -0
- package/.agent/skills/architecture-strategist/SKILL.md +52 -0
- package/.agent/skills/best-practices-researcher/SKILL.md +100 -0
- package/.agent/skills/bug-reproduction-validator/SKILL.md +67 -0
- package/.agent/skills/code-simplicity-reviewer/SKILL.md +85 -0
- package/.agent/skills/coding-tutor/.claude-plugin/plugin.json +9 -0
- package/.agent/skills/coding-tutor/README.md +37 -0
- package/.agent/skills/coding-tutor/commands/quiz-me.md +1 -0
- package/.agent/skills/coding-tutor/commands/sync-tutorials.md +25 -0
- package/.agent/skills/coding-tutor/commands/teach-me.md +1 -0
- package/.agent/skills/coding-tutor/skills/coding-tutor/SKILL.md +214 -0
- package/.agent/skills/coding-tutor/skills/coding-tutor/scripts/create_tutorial.py +202 -0
- package/.agent/skills/coding-tutor/skills/coding-tutor/scripts/index_tutorials.py +203 -0
- package/.agent/skills/coding-tutor/skills/coding-tutor/scripts/quiz_priority.py +190 -0
- package/.agent/skills/coding-tutor/skills/coding-tutor/scripts/setup_tutorials.py +132 -0
- package/.agent/skills/compound-docs/SKILL.md +510 -0
- package/.agent/skills/compound-docs/assets/critical-pattern-template.md +34 -0
- package/.agent/skills/compound-docs/assets/resolution-template.md +93 -0
- package/.agent/skills/compound-docs/references/yaml-schema.md +65 -0
- package/.agent/skills/compound-docs/schema.yaml +176 -0
- package/.agent/skills/create-agent-skills/SKILL.md +299 -0
- package/.agent/skills/create-agent-skills/references/api-security.md +226 -0
- package/.agent/skills/create-agent-skills/references/be-clear-and-direct.md +531 -0
- package/.agent/skills/create-agent-skills/references/best-practices.md +404 -0
- package/.agent/skills/create-agent-skills/references/common-patterns.md +595 -0
- package/.agent/skills/create-agent-skills/references/core-principles.md +437 -0
- package/.agent/skills/create-agent-skills/references/executable-code.md +175 -0
- package/.agent/skills/create-agent-skills/references/iteration-and-testing.md +474 -0
- package/.agent/skills/create-agent-skills/references/official-spec.md +185 -0
- package/.agent/skills/create-agent-skills/references/recommended-structure.md +168 -0
- package/.agent/skills/create-agent-skills/references/skill-structure.md +372 -0
- package/.agent/skills/create-agent-skills/references/using-scripts.md +113 -0
- package/.agent/skills/create-agent-skills/references/using-templates.md +112 -0
- package/.agent/skills/create-agent-skills/references/workflows-and-validation.md +510 -0
- package/.agent/skills/create-agent-skills/templates/router-skill.md +73 -0
- package/.agent/skills/create-agent-skills/templates/simple-skill.md +33 -0
- package/.agent/skills/create-agent-skills/workflows/add-reference.md +96 -0
- package/.agent/skills/create-agent-skills/workflows/add-script.md +93 -0
- package/.agent/skills/create-agent-skills/workflows/add-template.md +74 -0
- package/.agent/skills/create-agent-skills/workflows/add-workflow.md +120 -0
- package/.agent/skills/create-agent-skills/workflows/audit-skill.md +138 -0
- package/.agent/skills/create-agent-skills/workflows/create-domain-expertise-skill.md +605 -0
- package/.agent/skills/create-agent-skills/workflows/create-new-skill.md +191 -0
- package/.agent/skills/create-agent-skills/workflows/get-guidance.md +121 -0
- package/.agent/skills/create-agent-skills/workflows/upgrade-to-router.md +161 -0
- package/.agent/skills/create-agent-skills/workflows/verify-skill.md +204 -0
- package/.agent/skills/data-integrity-guardian/SKILL.md +70 -0
- package/.agent/skills/data-migration-expert/SKILL.md +97 -0
- package/.agent/skills/deployment-verification-agent/SKILL.md +159 -0
- package/.agent/skills/design-implementation-reviewer/SKILL.md +85 -0
- package/.agent/skills/design-iterator/SKILL.md +197 -0
- package/.agent/skills/dhh-rails-reviewer/SKILL.md +45 -0
- package/.agent/skills/dhh-rails-style/SKILL.md +184 -0
- package/.agent/skills/dhh-rails-style/references/architecture.md +653 -0
- package/.agent/skills/dhh-rails-style/references/controllers.md +303 -0
- package/.agent/skills/dhh-rails-style/references/frontend.md +510 -0
- package/.agent/skills/dhh-rails-style/references/gems.md +266 -0
- package/.agent/skills/dhh-rails-style/references/models.md +359 -0
- package/.agent/skills/dhh-rails-style/references/testing.md +338 -0
- package/.agent/skills/dspy-ruby/SKILL.md +594 -0
- package/.agent/skills/dspy-ruby/assets/config-template.rb +359 -0
- package/.agent/skills/dspy-ruby/assets/module-template.rb +326 -0
- package/.agent/skills/dspy-ruby/assets/signature-template.rb +143 -0
- package/.agent/skills/dspy-ruby/references/core-concepts.md +265 -0
- package/.agent/skills/dspy-ruby/references/optimization.md +623 -0
- package/.agent/skills/dspy-ruby/references/providers.md +305 -0
- package/.agent/skills/every-style-editor/SKILL.md +134 -0
- package/.agent/skills/every-style-editor/references/EVERY_WRITE_STYLE.md +529 -0
- package/.agent/skills/figma-design-sync/SKILL.md +166 -0
- package/.agent/skills/file-todos/SKILL.md +251 -0
- package/.agent/skills/file-todos/assets/todo-template.md +155 -0
- package/.agent/skills/framework-docs-researcher/SKILL.md +83 -0
- package/.agent/skills/frontend-design/SKILL.md +42 -0
- package/.agent/skills/gemini-imagegen/SKILL.md +237 -0
- package/.agent/skills/gemini-imagegen/requirements.txt +2 -0
- package/.agent/skills/gemini-imagegen/scripts/compose_images.py +168 -0
- package/.agent/skills/gemini-imagegen/scripts/edit_image.py +157 -0
- package/.agent/skills/gemini-imagegen/scripts/gemini_images.py +265 -0
- package/.agent/skills/gemini-imagegen/scripts/generate_image.py +147 -0
- package/.agent/skills/gemini-imagegen/scripts/multi_turn_chat.py +215 -0
- package/.agent/skills/git-history-analyzer/SKILL.md +42 -0
- package/.agent/skills/git-worktree/SKILL.md +302 -0
- package/.agent/skills/git-worktree/scripts/worktree-manager.sh +345 -0
- package/.agent/skills/julik-frontend-races-reviewer/SKILL.md +222 -0
- package/.agent/skills/kieran-python-reviewer/SKILL.md +104 -0
- package/.agent/skills/kieran-rails-reviewer/SKILL.md +86 -0
- package/.agent/skills/kieran-typescript-reviewer/SKILL.md +95 -0
- package/.agent/skills/lint/SKILL.md +16 -0
- package/.agent/skills/pattern-recognition-specialist/SKILL.md +57 -0
- package/.agent/skills/performance-oracle/SKILL.md +110 -0
- package/.agent/skills/pr-comment-resolver/SKILL.md +69 -0
- package/.agent/skills/rclone/SKILL.md +150 -0
- package/.agent/skills/rclone/scripts/check_setup.sh +60 -0
- package/.agent/skills/repo-research-analyst/SKILL.md +113 -0
- package/.agent/skills/security-sentinel/SKILL.md +93 -0
- package/.agent/skills/skill-creator/SKILL.md +209 -0
- package/.agent/skills/skill-creator/scripts/init_skill.py +304 -0
- package/.agent/skills/skill-creator/scripts/package_skill.py +112 -0
- package/.agent/skills/skill-creator/scripts/quick_validate.py +72 -0
- package/.agent/skills/spec-flow-analyzer/SKILL.md +113 -0
- package/.agent/skills/test-agent/SKILL.md +4 -0
- package/.agent/workflows/agent-native-audit.md +277 -0
- package/.agent/workflows/ask-user-question.md +21 -0
- package/.agent/workflows/changelog.md +137 -0
- package/.agent/workflows/compound.md +202 -0
- package/.agent/workflows/create-agent-skill.md +8 -0
- package/.agent/workflows/deepen-plan-research.md +334 -0
- package/.agent/workflows/deepen-plan-synthesis.md +182 -0
- package/.agent/workflows/deepen-plan.md +79 -0
- package/.agent/workflows/feature-video.md +342 -0
- package/.agent/workflows/generate-command.md +162 -0
- package/.agent/workflows/heal-skill.md +142 -0
- package/.agent/workflows/lfg.md +20 -0
- package/.agent/workflows/plan-analysis.md +67 -0
- package/.agent/workflows/plan-next-steps.md +63 -0
- package/.agent/workflows/plan-review.md +33 -0
- package/.agent/workflows/plan-synthesis.md +106 -0
- package/.agent/workflows/plan.md +49 -0
- package/.agent/workflows/report-bug.md +150 -0
- package/.agent/workflows/reproduce-bug.md +99 -0
- package/.agent/workflows/resolve-parallel.md +34 -0
- package/.agent/workflows/resolve-pr-parallel.md +49 -0
- package/.agent/workflows/resolve-todo-parallel.md +35 -0
- package/.agent/workflows/review-analysis.md +145 -0
- package/.agent/workflows/review-synthesis.md +262 -0
- package/.agent/workflows/review.md +64 -0
- package/.agent/workflows/ship.md +90 -0
- package/.agent/workflows/test-command.md +3 -0
- package/.agent/workflows/triage.md +310 -0
- package/.agent/workflows/work.md +157 -0
- package/.agent/workflows/xcode-test.md +332 -0
- package/LICENSE +22 -0
- package/README.md +49 -0
- package/bin/ag-cortex.js +54 -0
- package/lib/core.js +165 -0
- package/package.json +31 -0
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
<overview>
|
|
2
|
+
How to design MCP tools following prompt-native principles. Tools should be primitives that enable capability, not workflows that encode decisions.
|
|
3
|
+
|
|
4
|
+
**Core principle:** Whatever a user can do, the agent should be able to do. Don't artificially limit the agent—give it the same primitives a power user would have.
|
|
5
|
+
</overview>
|
|
6
|
+
|
|
7
|
+
<principle name="primitives-not-workflows">
|
|
8
|
+
## Tools Are Primitives, Not Workflows
|
|
9
|
+
|
|
10
|
+
**Wrong approach:** Tools that encode business logic
|
|
11
|
+
```typescript
|
|
12
|
+
tool("process_feedback", {
|
|
13
|
+
feedback: z.string(),
|
|
14
|
+
category: z.enum(["bug", "feature", "question"]),
|
|
15
|
+
priority: z.enum(["low", "medium", "high"]),
|
|
16
|
+
}, async ({ feedback, category, priority }) => {
|
|
17
|
+
// Tool decides how to process
|
|
18
|
+
const processed = categorize(feedback);
|
|
19
|
+
const stored = await saveToDatabase(processed);
|
|
20
|
+
const notification = await notify(priority);
|
|
21
|
+
return { processed, stored, notification };
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Right approach:** Primitives that enable any workflow
|
|
26
|
+
```typescript
|
|
27
|
+
tool("store_item", {
|
|
28
|
+
key: z.string(),
|
|
29
|
+
value: z.any(),
|
|
30
|
+
}, async ({ key, value }) => {
|
|
31
|
+
await db.set(key, value);
|
|
32
|
+
return { text: `Stored ${key}` };
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
tool("send_message", {
|
|
36
|
+
channel: z.string(),
|
|
37
|
+
content: z.string(),
|
|
38
|
+
}, async ({ channel, content }) => {
|
|
39
|
+
await messenger.send(channel, content);
|
|
40
|
+
return { text: "Sent" };
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The agent decides categorization, priority, and when to notify based on the system prompt.
|
|
45
|
+
</principle>
|
|
46
|
+
|
|
47
|
+
<principle name="descriptive-names">
|
|
48
|
+
## Tools Should Have Descriptive, Primitive Names
|
|
49
|
+
|
|
50
|
+
Names should describe the capability, not the use case:
|
|
51
|
+
|
|
52
|
+
| Wrong | Right |
|
|
53
|
+
|-------|-------|
|
|
54
|
+
| `process_user_feedback` | `store_item` |
|
|
55
|
+
| `create_feedback_summary` | `write_file` |
|
|
56
|
+
| `send_notification` | `send_message` |
|
|
57
|
+
| `deploy_to_production` | `git_push` |
|
|
58
|
+
|
|
59
|
+
The prompt tells the agent *when* to use primitives. The tool just provides *capability*.
|
|
60
|
+
</principle>
|
|
61
|
+
|
|
62
|
+
<principle name="simple-inputs">
|
|
63
|
+
## Inputs Should Be Simple
|
|
64
|
+
|
|
65
|
+
Tools accept data. They don't accept decisions.
|
|
66
|
+
|
|
67
|
+
**Wrong:** Tool accepts decisions
|
|
68
|
+
```typescript
|
|
69
|
+
tool("format_content", {
|
|
70
|
+
content: z.string(),
|
|
71
|
+
format: z.enum(["markdown", "html", "json"]),
|
|
72
|
+
style: z.enum(["formal", "casual", "technical"]),
|
|
73
|
+
}, ...)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Right:** Tool accepts data, agent decides format
|
|
77
|
+
```typescript
|
|
78
|
+
tool("write_file", {
|
|
79
|
+
path: z.string(),
|
|
80
|
+
content: z.string(),
|
|
81
|
+
}, ...)
|
|
82
|
+
// Agent decides to write index.html with HTML content, or data.json with JSON
|
|
83
|
+
```
|
|
84
|
+
</principle>
|
|
85
|
+
|
|
86
|
+
<principle name="rich-outputs">
|
|
87
|
+
## Outputs Should Be Rich
|
|
88
|
+
|
|
89
|
+
Return enough information for the agent to verify and iterate.
|
|
90
|
+
|
|
91
|
+
**Wrong:** Minimal output
|
|
92
|
+
```typescript
|
|
93
|
+
async ({ key }) => {
|
|
94
|
+
await db.delete(key);
|
|
95
|
+
return { text: "Deleted" };
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Right:** Rich output
|
|
100
|
+
```typescript
|
|
101
|
+
async ({ key }) => {
|
|
102
|
+
const existed = await db.has(key);
|
|
103
|
+
if (!existed) {
|
|
104
|
+
return { text: `Key ${key} did not exist` };
|
|
105
|
+
}
|
|
106
|
+
await db.delete(key);
|
|
107
|
+
return { text: `Deleted ${key}. ${await db.count()} items remaining.` };
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
</principle>
|
|
111
|
+
|
|
112
|
+
<design_template>
|
|
113
|
+
## Tool Design Template
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { createSdkMcpServer, tool } from "@antigravity/agent-sdk";
|
|
117
|
+
import { z } from "zod";
|
|
118
|
+
|
|
119
|
+
export const serverName = createSdkMcpServer({
|
|
120
|
+
name: "server-name",
|
|
121
|
+
version: "1.0.0",
|
|
122
|
+
tools: [
|
|
123
|
+
// READ operations
|
|
124
|
+
tool(
|
|
125
|
+
"read_item",
|
|
126
|
+
"Read an item by key",
|
|
127
|
+
{ key: z.string().describe("Item key") },
|
|
128
|
+
async ({ key }) => {
|
|
129
|
+
const item = await storage.get(key);
|
|
130
|
+
return {
|
|
131
|
+
content: [{
|
|
132
|
+
type: "text",
|
|
133
|
+
text: item ? JSON.stringify(item, null, 2) : `Not found: ${key}`,
|
|
134
|
+
}],
|
|
135
|
+
isError: !item,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
),
|
|
139
|
+
|
|
140
|
+
tool(
|
|
141
|
+
"list_items",
|
|
142
|
+
"List all items, optionally filtered",
|
|
143
|
+
{
|
|
144
|
+
prefix: z.string().optional().describe("Filter by key prefix"),
|
|
145
|
+
limit: z.number().default(100).describe("Max items"),
|
|
146
|
+
},
|
|
147
|
+
async ({ prefix, limit }) => {
|
|
148
|
+
const items = await storage.list({ prefix, limit });
|
|
149
|
+
return {
|
|
150
|
+
content: [{
|
|
151
|
+
type: "text",
|
|
152
|
+
text: `Found ${items.length} items:\n${items.map(i => i.key).join("\n")}`,
|
|
153
|
+
}],
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
),
|
|
157
|
+
|
|
158
|
+
// WRITE operations
|
|
159
|
+
tool(
|
|
160
|
+
"store_item",
|
|
161
|
+
"Store an item",
|
|
162
|
+
{
|
|
163
|
+
key: z.string().describe("Item key"),
|
|
164
|
+
value: z.any().describe("Item data"),
|
|
165
|
+
},
|
|
166
|
+
async ({ key, value }) => {
|
|
167
|
+
await storage.set(key, value);
|
|
168
|
+
return {
|
|
169
|
+
content: [{ type: "text", text: `Stored ${key}` }],
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
),
|
|
173
|
+
|
|
174
|
+
tool(
|
|
175
|
+
"delete_item",
|
|
176
|
+
"Delete an item",
|
|
177
|
+
{ key: z.string().describe("Item key") },
|
|
178
|
+
async ({ key }) => {
|
|
179
|
+
const existed = await storage.delete(key);
|
|
180
|
+
return {
|
|
181
|
+
content: [{
|
|
182
|
+
type: "text",
|
|
183
|
+
text: existed ? `Deleted ${key}` : `${key} did not exist`,
|
|
184
|
+
}],
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
),
|
|
188
|
+
|
|
189
|
+
// EXTERNAL operations
|
|
190
|
+
tool(
|
|
191
|
+
"call_api",
|
|
192
|
+
"Make an HTTP request",
|
|
193
|
+
{
|
|
194
|
+
url: z.string().url(),
|
|
195
|
+
method: z.enum(["GET", "POST", "PUT", "DELETE"]).default("GET"),
|
|
196
|
+
body: z.any().optional(),
|
|
197
|
+
},
|
|
198
|
+
async ({ url, method, body }) => {
|
|
199
|
+
const response = await fetch(url, { method, body: JSON.stringify(body) });
|
|
200
|
+
const text = await response.text();
|
|
201
|
+
return {
|
|
202
|
+
content: [{
|
|
203
|
+
type: "text",
|
|
204
|
+
text: `${response.status} ${response.statusText}\n\n${text}`,
|
|
205
|
+
}],
|
|
206
|
+
isError: !response.ok,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
),
|
|
210
|
+
],
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
</design_template>
|
|
214
|
+
|
|
215
|
+
<example name="feedback-server">
|
|
216
|
+
## Example: Feedback Storage Server
|
|
217
|
+
|
|
218
|
+
This server provides primitives for storing feedback. It does NOT decide how to categorize or organize feedback—that's the agent's job via the prompt.
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
export const feedbackMcpServer = createSdkMcpServer({
|
|
222
|
+
name: "feedback",
|
|
223
|
+
version: "1.0.0",
|
|
224
|
+
tools: [
|
|
225
|
+
tool(
|
|
226
|
+
"store_feedback",
|
|
227
|
+
"Store a feedback item",
|
|
228
|
+
{
|
|
229
|
+
item: z.object({
|
|
230
|
+
id: z.string(),
|
|
231
|
+
author: z.string(),
|
|
232
|
+
content: z.string(),
|
|
233
|
+
importance: z.number().min(1).max(5),
|
|
234
|
+
timestamp: z.string(),
|
|
235
|
+
status: z.string().optional(),
|
|
236
|
+
urls: z.array(z.string()).optional(),
|
|
237
|
+
metadata: z.any().optional(),
|
|
238
|
+
}).describe("Feedback item"),
|
|
239
|
+
},
|
|
240
|
+
async ({ item }) => {
|
|
241
|
+
await db.feedback.insert(item);
|
|
242
|
+
return {
|
|
243
|
+
content: [{
|
|
244
|
+
type: "text",
|
|
245
|
+
text: `Stored feedback ${item.id} from ${item.author}`,
|
|
246
|
+
}],
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
),
|
|
250
|
+
|
|
251
|
+
tool(
|
|
252
|
+
"list_feedback",
|
|
253
|
+
"List feedback items",
|
|
254
|
+
{
|
|
255
|
+
limit: z.number().default(50),
|
|
256
|
+
status: z.string().optional(),
|
|
257
|
+
},
|
|
258
|
+
async ({ limit, status }) => {
|
|
259
|
+
const items = await db.feedback.list({ limit, status });
|
|
260
|
+
return {
|
|
261
|
+
content: [{
|
|
262
|
+
type: "text",
|
|
263
|
+
text: JSON.stringify(items, null, 2),
|
|
264
|
+
}],
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
),
|
|
268
|
+
|
|
269
|
+
tool(
|
|
270
|
+
"update_feedback",
|
|
271
|
+
"Update a feedback item",
|
|
272
|
+
{
|
|
273
|
+
id: z.string(),
|
|
274
|
+
updates: z.object({
|
|
275
|
+
status: z.string().optional(),
|
|
276
|
+
importance: z.number().optional(),
|
|
277
|
+
metadata: z.any().optional(),
|
|
278
|
+
}),
|
|
279
|
+
},
|
|
280
|
+
async ({ id, updates }) => {
|
|
281
|
+
await db.feedback.update(id, updates);
|
|
282
|
+
return {
|
|
283
|
+
content: [{ type: "text", text: `Updated ${id}` }],
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
),
|
|
287
|
+
],
|
|
288
|
+
});
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
The system prompt then tells the agent *how* to use these primitives:
|
|
292
|
+
|
|
293
|
+
```markdown
|
|
294
|
+
## Feedback Processing
|
|
295
|
+
|
|
296
|
+
When someone shares feedback:
|
|
297
|
+
1. Extract author, content, and any URLs
|
|
298
|
+
2. Rate importance 1-5 based on actionability
|
|
299
|
+
3. Store using feedback.store_feedback
|
|
300
|
+
4. If high importance (4-5), notify the channel
|
|
301
|
+
|
|
302
|
+
Use your judgment about importance ratings.
|
|
303
|
+
```
|
|
304
|
+
</example>
|
|
305
|
+
|
|
306
|
+
<principle name="dynamic-capability-discovery">
|
|
307
|
+
## Dynamic Capability Discovery vs Static Tool Mapping
|
|
308
|
+
|
|
309
|
+
**This pattern is specifically for agent-native apps** where you want the agent to have full access to an external API—the same access a user would have. It follows the core agent-native principle: "Whatever the user can do, the agent can do."
|
|
310
|
+
|
|
311
|
+
If you're building a constrained agent with limited capabilities, static tool mapping may be intentional. But for agent-native apps integrating with HealthKit, HomeKit, GraphQL, or similar APIs:
|
|
312
|
+
|
|
313
|
+
**Static Tool Mapping (Anti-pattern for Agent-Native):**
|
|
314
|
+
Build individual tools for each API capability. Always out of date, limits agent to only what you anticipated.
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
// ❌ Static: Every API type needs a hardcoded tool
|
|
318
|
+
tool("read_steps", async ({ startDate, endDate }) => {
|
|
319
|
+
return healthKit.query(HKQuantityType.stepCount, startDate, endDate);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
tool("read_heart_rate", async ({ startDate, endDate }) => {
|
|
323
|
+
return healthKit.query(HKQuantityType.heartRate, startDate, endDate);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
tool("read_sleep", async ({ startDate, endDate }) => {
|
|
327
|
+
return healthKit.query(HKCategoryType.sleepAnalysis, startDate, endDate);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// When HealthKit adds glucose tracking... you need a code change
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
**Dynamic Capability Discovery (Preferred):**
|
|
334
|
+
Build a meta-tool that discovers what's available, and a generic tool that can access anything.
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
// ✅ Dynamic: Agent discovers and uses any capability
|
|
338
|
+
|
|
339
|
+
// Discovery tool - returns what's available at runtime
|
|
340
|
+
tool("list_available_capabilities", async () => {
|
|
341
|
+
const quantityTypes = await healthKit.availableQuantityTypes();
|
|
342
|
+
const categoryTypes = await healthKit.availableCategoryTypes();
|
|
343
|
+
|
|
344
|
+
return {
|
|
345
|
+
text: `Available health metrics:\n` +
|
|
346
|
+
`Quantity types: ${quantityTypes.join(", ")}\n` +
|
|
347
|
+
`Category types: ${categoryTypes.join(", ")}\n` +
|
|
348
|
+
`\nUse read_health_data with any of these types.`
|
|
349
|
+
};
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
// Generic access tool - type is a string, API validates
|
|
353
|
+
tool("read_health_data", {
|
|
354
|
+
dataType: z.string(), // NOT z.enum - let HealthKit validate
|
|
355
|
+
startDate: z.string(),
|
|
356
|
+
endDate: z.string(),
|
|
357
|
+
aggregation: z.enum(["sum", "average", "samples"]).optional()
|
|
358
|
+
}, async ({ dataType, startDate, endDate, aggregation }) => {
|
|
359
|
+
// HealthKit validates the type, returns helpful error if invalid
|
|
360
|
+
const result = await healthKit.query(dataType, startDate, endDate, aggregation);
|
|
361
|
+
return { text: JSON.stringify(result, null, 2) };
|
|
362
|
+
});
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
**When to Use Each Approach:**
|
|
366
|
+
|
|
367
|
+
| Dynamic (Agent-Native) | Static (Constrained Agent) |
|
|
368
|
+
|------------------------|---------------------------|
|
|
369
|
+
| Agent should access anything user can | Agent has intentionally limited scope |
|
|
370
|
+
| External API with many endpoints (HealthKit, HomeKit, GraphQL) | Internal domain with fixed operations |
|
|
371
|
+
| API evolves independently of your code | Tightly coupled domain logic |
|
|
372
|
+
| You want full action parity | You want strict guardrails |
|
|
373
|
+
|
|
374
|
+
**The agent-native default is Dynamic.** Only use Static when you're intentionally limiting the agent's capabilities.
|
|
375
|
+
|
|
376
|
+
**Complete Dynamic Pattern:**
|
|
377
|
+
|
|
378
|
+
```swift
|
|
379
|
+
// 1. Discovery tool: What can I access?
|
|
380
|
+
tool("list_health_types", "Get available health data types") { _ in
|
|
381
|
+
let store = HKHealthStore()
|
|
382
|
+
|
|
383
|
+
let quantityTypes = HKQuantityTypeIdentifier.allCases.map { $0.rawValue }
|
|
384
|
+
let categoryTypes = HKCategoryTypeIdentifier.allCases.map { $0.rawValue }
|
|
385
|
+
let characteristicTypes = HKCharacteristicTypeIdentifier.allCases.map { $0.rawValue }
|
|
386
|
+
|
|
387
|
+
return ToolResult(text: """
|
|
388
|
+
Available HealthKit types:
|
|
389
|
+
|
|
390
|
+
## Quantity Types (numeric values)
|
|
391
|
+
\(quantityTypes.joined(separator: ", "))
|
|
392
|
+
|
|
393
|
+
## Category Types (categorical data)
|
|
394
|
+
\(categoryTypes.joined(separator: ", "))
|
|
395
|
+
|
|
396
|
+
## Characteristic Types (user info)
|
|
397
|
+
\(characteristicTypes.joined(separator: ", "))
|
|
398
|
+
|
|
399
|
+
Use read_health_data or write_health_data with any of these.
|
|
400
|
+
""")
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// 2. Generic read: Access any type by name
|
|
404
|
+
tool("read_health_data", "Read any health metric", {
|
|
405
|
+
dataType: z.string().describe("Type name from list_health_types"),
|
|
406
|
+
startDate: z.string(),
|
|
407
|
+
endDate: z.string()
|
|
408
|
+
}) { request in
|
|
409
|
+
// Let HealthKit validate the type name
|
|
410
|
+
guard let type = HKQuantityTypeIdentifier(rawValue: request.dataType)
|
|
411
|
+
?? HKCategoryTypeIdentifier(rawValue: request.dataType) else {
|
|
412
|
+
return ToolResult(
|
|
413
|
+
text: "Unknown type: \(request.dataType). Use list_health_types to see available types.",
|
|
414
|
+
isError: true
|
|
415
|
+
)
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
let samples = try await healthStore.querySamples(type: type, start: startDate, end: endDate)
|
|
419
|
+
return ToolResult(text: samples.formatted())
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// 3. Context injection: Tell agent what's available in system prompt
|
|
423
|
+
func buildSystemPrompt() -> String {
|
|
424
|
+
let availableTypes = healthService.getAuthorizedTypes()
|
|
425
|
+
|
|
426
|
+
return """
|
|
427
|
+
## Available Health Data
|
|
428
|
+
|
|
429
|
+
You have access to these health metrics:
|
|
430
|
+
\(availableTypes.map { "- \($0)" }.joined(separator: "\n"))
|
|
431
|
+
|
|
432
|
+
Use read_health_data with any type above. For new types not listed,
|
|
433
|
+
use list_health_types to discover what's available.
|
|
434
|
+
"""
|
|
435
|
+
}
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
**Benefits:**
|
|
439
|
+
- Agent can use any API capability, including ones added after your code shipped
|
|
440
|
+
- API is the validator, not your enum definition
|
|
441
|
+
- Smaller tool surface (2-3 tools vs N tools)
|
|
442
|
+
- Agent naturally discovers capabilities by asking
|
|
443
|
+
- Works with any API that has introspection (HealthKit, GraphQL, OpenAPI)
|
|
444
|
+
</principle>
|
|
445
|
+
|
|
446
|
+
<principle name="crud-completeness">
|
|
447
|
+
## CRUD Completeness
|
|
448
|
+
|
|
449
|
+
Every data type the agent can create, it should be able to read, update, and delete. Incomplete CRUD = broken action parity.
|
|
450
|
+
|
|
451
|
+
**Anti-pattern: Create-only tools**
|
|
452
|
+
```typescript
|
|
453
|
+
// ❌ Can create but not modify or delete
|
|
454
|
+
tool("create_experiment", { hypothesis, variable, metric })
|
|
455
|
+
tool("write_journal_entry", { content, author, tags })
|
|
456
|
+
// User: "Delete that experiment" → Agent: "I can't do that"
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
**Correct: Full CRUD for each entity**
|
|
460
|
+
```typescript
|
|
461
|
+
// ✅ Complete CRUD
|
|
462
|
+
tool("create_experiment", { hypothesis, variable, metric })
|
|
463
|
+
tool("read_experiment", { id })
|
|
464
|
+
tool("update_experiment", { id, updates: { hypothesis?, status?, endDate? } })
|
|
465
|
+
tool("delete_experiment", { id })
|
|
466
|
+
|
|
467
|
+
tool("create_journal_entry", { content, author, tags })
|
|
468
|
+
tool("read_journal", { query?, dateRange?, author? })
|
|
469
|
+
tool("update_journal_entry", { id, content, tags? })
|
|
470
|
+
tool("delete_journal_entry", { id })
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
**The CRUD Audit:**
|
|
474
|
+
For each entity type in your app, verify:
|
|
475
|
+
- [ ] Create: Agent can create new instances
|
|
476
|
+
- [ ] Read: Agent can query/search/list instances
|
|
477
|
+
- [ ] Update: Agent can modify existing instances
|
|
478
|
+
- [ ] Delete: Agent can remove instances
|
|
479
|
+
|
|
480
|
+
If any operation is missing, users will eventually ask for it and the agent will fail.
|
|
481
|
+
</principle>
|
|
482
|
+
|
|
483
|
+
<checklist>
|
|
484
|
+
## MCP Tool Design Checklist
|
|
485
|
+
|
|
486
|
+
**Fundamentals:**
|
|
487
|
+
- [ ] Tool names describe capability, not use case
|
|
488
|
+
- [ ] Inputs are data, not decisions
|
|
489
|
+
- [ ] Outputs are rich (enough for agent to verify)
|
|
490
|
+
- [ ] CRUD operations are separate tools (not one mega-tool)
|
|
491
|
+
- [ ] No business logic in tool implementations
|
|
492
|
+
- [ ] Error states clearly communicated via `isError`
|
|
493
|
+
- [ ] Descriptions explain what the tool does, not when to use it
|
|
494
|
+
|
|
495
|
+
**Dynamic Capability Discovery (for agent-native apps):**
|
|
496
|
+
- [ ] For external APIs where agent should have full access, use dynamic discovery
|
|
497
|
+
- [ ] Include a `list_*` or `discover_*` tool for each API surface
|
|
498
|
+
- [ ] Use string inputs (not enums) when the API validates
|
|
499
|
+
- [ ] Inject available capabilities into system prompt at runtime
|
|
500
|
+
- [ ] Only use static tool mapping if intentionally limiting agent scope
|
|
501
|
+
|
|
502
|
+
**CRUD Completeness:**
|
|
503
|
+
- [ ] Every entity has create, read, update, delete operations
|
|
504
|
+
- [ ] Every UI action has a corresponding agent tool
|
|
505
|
+
- [ ] Test: "Can the agent undo what it just did?"
|
|
506
|
+
</checklist>
|