Skill
All MCP tools that author, read, and invoke skills.
Tools for authoring, reading, and invoking skills. The full external contract lives at sprout://skill/authoring-guide.
Skills carry a category: generic (default, runnable by either agent) or home_agent (refused at skill.invoke and heartbeat.create on Sprout's side; only meaningful when your home agent is the runner). See Skill for the conceptual page.
skill.writeSafe to retry
Author a new skill. Two-step commit via dry-run + spec-hash for auditable commit.
Scope: skill:write
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
name | string | yes | Library label. |
description | string | yes | Parent-language summary. |
category | "generic" | "home_agent" | no | Default generic. |
prompt | string | yes | The procedure your agent reads on invoke. Uses {{input.*}} placeholders. |
handsReferenced | string[] | no | Tool names declared as used. Underscore form (task_create). |
inputVariables | object[] | no | [{ name, type?, description? }]. |
kidCallable | boolean | no | If true, kid app can launch this. Extra scrutiny. |
canvasIds | string[] | no | Canvas UUIDs the skill links. |
ageRange | string | no | e.g. "5-7". |
dryRun | boolean | no | If true, returns preview + specHash without writing. |
specHash | string | no | Echoed from a prior dry-run. Required for commit after dry-run. |
Request example
{
"method": "tools/call",
"params": {
"name": "skill.write",
"arguments": {
"name": "Refresh today's check-in",
"description": "Pull today's school events and rewrite the check-in chat to match.",
"category": "home_agent",
"prompt": "For {{input.child_name}} on {{input.today}}: read events from the school connector, find the open Daily check-in task, rewrite conversationSpec.guidance to fit today.",
"handsReferenced": ["task_list", "task_update"],
"inputVariables": [
{ "name": "child_name" },
{ "name": "child_id" },
{ "name": "today" }
]
}
}
}Returns. { skillId } on commit; { previewSkill, policyDecision, specHash, nextStep } on dryRun.
Response example
{
"result": {
"skillId": "sk_2f...",
"nextStep": "Invoke with skill.invoke({skillId, input}) or schedule with heartbeat.create."
}
}Errors
BAD_INPUT PII_IN_PROMPT: A real child's name appeared verbatim inpromptordescription.BAD_INPUT SPEC_HASH_MISMATCH: Input changed between dry-run and commit.BAD_INPUT LEGACY_CANVAS_NO_DIMENSIONS: Attempted to attach a pre-V1 canvas without dimensions.
skill.update
Partial patch. Conditional gates: name/desc-only skips safety + policy.
Scope: skill:write
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
skillId | string | yes | The skill to update. |
name / description | string | no | Ownership-only gate. |
instructions | string | no | Ownership + safety gate. |
kidCallable / ageRange | various | no | Ownership + policy gate. |
addCanvasIds | string[] | no | Canvases to add. Incremental, not full-replacement. |
removeCanvasIds | string[] | no | Canvases to remove. Applied before adds. |
Request example
{
"method": "tools/call",
"params": {
"name": "skill.update",
"arguments": {
"skillId": "sk_2f...",
"description": "Updated description.",
"addCanvasIds": ["cv_19..."]
}
}
}Returns. { skillId, changedFields }
Response example
{
"result": {
"skillId": "sk_2f...",
"changedFields": ["description", "canvasIds"]
}
}Errors
BAD_INPUT CANVAS_OPS_CONFLICT: Same id inaddandremove.BAD_INPUT CANVAS_LIMIT_EXCEEDED: Post-mutation total > 5 canvases.BAD_INPUT CANVAS_NOT_IN_SKILL:removeCanvasIdsid not currently linked.
skill.list
Keyset-paginated summary list. Each item carries identity, status, author tier, kid-callability, plus canvasIds: string[]. No per-canvas metadata; no steps body; no inline canvas content.
Scope: skill:read
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
cursor | string | no | From a prior response. |
limit | int | no | Default 50. |
includeArchived | boolean | no | Default false. |
category | "generic" | "home_agent" | no | Filter. |
Request example
{
"method": "tools/call",
"params": {
"name": "skill.list",
"arguments": { "category": "home_agent", "limit": 50 }
}
}Returns. { items: SkillListItem[], nextCursor: string | null }
Response example
{
"result": {
"items": [
{
"skillId": "sk_2f...",
"name": "Refresh today's check-in",
"category": "home_agent",
"archived": false,
"canvasIds": [],
"lastTriggeredAt": "2026-05-23T08:00:00-04:00"
}
],
"nextCursor": null
}
}skill.get
Full skill row plus denormalized canvas metadata for chooser rendering.
Scope: skill:read
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
skillId | string | yes | The skill. |
Request example
{
"method": "tools/call",
"params": {
"name": "skill.get",
"arguments": { "skillId": "sk_2f..." }
}
}Returns. { skill, canvases: [{ canvasId, name, emoji }] }
Response example
{
"result": {
"skill": {
"skillId": "sk_2f...",
"name": "Refresh today's check-in",
"description": "...",
"category": "home_agent",
"prompt": "For {{input.child_name}}...",
"handsReferenced": ["task_list", "task_update"],
"inputVariables": [{ "name": "child_name" }, { "name": "child_id" }, { "name": "today" }]
},
"canvases": []
}
}Notes. Canvas metadata is a point-in-time snapshot. If a canvas is renamed after caching skill.get, the cached name/emoji is stale.
skill.invoke
Render the skill body into your conversation. Returns the rendered prompt with {{input.*}} substituted, plus canvas metadata. Render-only: invocation does not consume gems or fire kid-side notifications by itself.
Scope: skill:write
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
skillId | string | yes | The skill to invoke. |
input | object | no | Key-value map filling inputVariables. |
Request example
{
"method": "tools/call",
"params": {
"name": "skill.invoke",
"arguments": {
"skillId": "sk_2f...",
"input": {
"child_name": "Jay",
"child_id": "a4b9-...",
"today": "2026-05-23"
}
}
}
}Returns. { renderedPrompt: string, canvases: [{ canvasId, name, emoji }] }
Response example
{
"result": {
"renderedPrompt": "For Jay on 2026-05-23: read events from the school connector, find the open Daily check-in task, rewrite conversationSpec.guidance to fit today.",
"canvases": []
}
}Errors
PERMISSION_DENIED:category: "home_agent"skill refused at Sprout-side invoke. Run it in your agent instead.
skill.post_result
Publish a structured result to the family inbox. Used when your agent finishes a skill run and wants to deliver an auditable, parent-readable card.
Scope: skill:write
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
skillId | string | yes | The skill whose result is being posted. |
result | object | yes | Structured payload. Schema follows the skill's declared response model. |
Request example
{
"method": "tools/call",
"params": {
"name": "skill.post_result",
"arguments": {
"skillId": "sk_2f...",
"result": {
"summary": "Refreshed check-in guidance for Jay's PE day.",
"data": { "taskId": "01c0-...", "newGuidance": "..." }
}
}
}
}Returns. { resultId, threadId, postedAt }
Response example
{
"result": {
"resultId": "rs_8d...",
"threadId": "th_4a...",
"postedAt": "2026-05-23T08:00:01-04:00"
}
}Notes. Heartbeat-fired results post automatically via postResultNotify. Use skill.post_result for on-demand publishes from your own runs.
See also
- Model: Skill
- Start: Save it as a skill
- Authoring guide resource:
resources/read sprout://skill/authoring-guide