Skip to main content
Glama

Recipe Save

recipe_save
Destructive

Saves a reusable SKILL — a named recipe (an ordered sequence of LMCP tool calls with parameters) — to this Mac so the user can re-run it anytime with recipe_run. A user would save one to turn a multi-step LMCP workflow they repeat (a morning brief, inbox triage, a weekly report, a data pull) into a single reusable command. Saved skills can be shared with other people via recipe_export. The manifest must have a name and a non-empty steps array. A step cannot confirm itself: confirm in a step's args is removed when saving (confirm_removed_steps says how many steps had one) and ignored when running, so a step whose tool asks for confirmation answers with its preview instead of executing. A name that is already a recipe is refused with recipe_exists and nothing is written; pass overwrite: true to replace it. Returns {name}.

Input Schema

NameRequiredDescriptionDefault
manifestYesThe recipe manifest. Shape: {"name": string (required), "description": string, "params": [{"name": string, "type": "string"|"int"|"bool", "default": any}], "steps": [ ... ] (required, non-empty), "outputs": [{"kind": string}]}. A step is EITHER a tool call {"tool": <tool_name>, "args": {...}} OR a pause {"wait": {"seconds": N}} (also {"wait": N}). A tool-call step may add "optional": true (a boolean; default false): if it fails, recipe_run records {ok:false, error} for it, lists it in skipped_optional and carries on with the next step, instead of stopping — use it for a source the user may not have (an app, a permission). Any arg string may interpolate a declared param with ${name} (e.g. "limit": "${count}"); an arg that is exactly "${name}" keeps the param's type. Steps run in order; a later step can consume an EARLIER step's output with "${steps[N].result.KEY}" (0-based; supports .key and [i], e.g. "account": "${steps[0].result.accounts[0].name}"). A step cannot confirm itself: do not put `confirm` in a step's args (it is removed when saving and ignored when running). recipe_run runs the steps as they are; a step whose tool asks first (a send, a permanent delete, a publication) answers with its preview and is not executed. A step that names a note should give note_name (the note's title): a note_id only names the note on the Mac that issued it, so a step that gives both is saved with note_name alone.
overwriteNoMust be true to replace a recipe that already has this name. Without it, a taken name is refused with recipe_exists and the existing recipe is left as it was.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changed
    • changedInput schema / properties / manifest / description
      Previous value: -"The recipe manifest. Shape: {\"name\": string (required), \"description\": string, \"params\": [{\"name\": string, \"type\": \"string\"|\"int\"|\"bool\", \"default\": any}], \"steps\": [ ... ] (required, non-empty), \"outputs\": [{\"kind\": string}]}. A step is EITHER a tool call {\"tool\": <tool_name>, \"args\": {...}} OR a pause {\"wait\": {\"seconds\": N}} (also {\"wait\": N}). A tool-call step may add \"optional\": true (a boolean; default false): if it fails, recipe_run records {ok:false, error} for it, lists it in skipped_optional and carries on with the next step, instead of stopping — use it for a source the user may not have (an app, a permission). Any arg string may interpolate a declared param with ${name} (e.g. \"limit\": \"${count}\"); an arg that is exactly \"${name}\" keeps the param's type. Steps run in order; a later step can consume an EARLIER step's output with \"${steps[N].result.KEY}\" (0-based; supports .key and [i], e.g. \"account\": \"${steps[0].result.accounts[0].name}\"). A step cannot confirm itself: do not put `confirm` in a step's args (it is removed when saving and ignored when running). recipe_run asks for one confirmation when the recipe has state-changing steps; a step whose tool asks for its own confirmation then answers with its preview and is not executed. A step that names a note should give note_name (the note's title): a note_id only names the note on the Mac that issued it, so a step that gives both is saved with note_name alone."New value: +"The recipe manifest. Shape: {\"name\": string (required), \"description\": string, \"params\": [{\"name\": string, \"type\": \"string\"|\"int\"|\"bool\", \"default\": any}], \"steps\": [ ... ] (required, non-empty), \"outputs\": [{\"kind\": string}]}. A step is EITHER a tool call {\"tool\": <tool_name>, \"args\": {...}} OR a pause {\"wait\": {\"seconds\": N}} (also {\"wait\": N}). A tool-call step may add \"optional\": true (a boolean; default false): if it fails, recipe_run records {ok:false, error} for it, lists it in skipped_optional and carries on with the next step, instead of stopping — use it for a source the user may not have (an app, a permission). Any arg string may interpolate a declared param with ${name} (e.g. \"limit\": \"${count}\"); an arg that is exactly \"${name}\" keeps the param's type. Steps run in order; a later step can consume an EARLIER step's output with \"${steps[N].result.KEY}\" (0-based; supports .key and [i], e.g. \"account\": \"${steps[0].result.accounts[0].name}\"). A step cannot confirm itself: do not put `confirm` in a step's args (it is removed when saving and ignored when running). recipe_run runs the steps as they are; a step whose tool asks first (a send, a permanent delete, a publication) answers with its preview and is not executed. A step that names a note should give note_name (the note's title): a note_id only names the note on the Mac that issued it, so a step that gives both is saved with note_name alone."
  2. Changed2 schema fields changed
    • changedInput schema / properties / manifest / description
      Previous value: -"The recipe manifest. Shape: {\"name\": string (required), \"description\": string, \"params\": [{\"name\": string, \"type\": \"string\"|\"int\"|\"bool\", \"default\": any}], \"steps\": [ ... ] (required, non-empty), \"outputs\": [{\"kind\": string}]}. A step is EITHER a tool call {\"tool\": <tool_name>, \"args\": {...}} OR a pause {\"wait\": {\"seconds\": N}} (also {\"wait\": N}). A tool-call step may add \"optional\": true (a boolean; default false): if it fails, recipe_run records {ok:false, error} for it, lists it in skipped_optional and carries on with the next step, instead of stopping — use it for a source the user may not have (an app, a permission). Any arg string may interpolate a declared param with ${name} (e.g. \"limit\": \"${count}\"); an arg that is exactly \"${name}\" keeps the param's type. Steps run in order; a later step can consume an EARLIER step's output with \"${steps[N].result.KEY}\" (0-based; supports .key and [i], e.g. \"account\": \"${steps[0].result.accounts[0].name}\"). On recipe_run a state-changing step previews first unless confirm:true."New value: +"The recipe manifest. Shape: {\"name\": string (required), \"description\": string, \"params\": [{\"name\": string, \"type\": \"string\"|\"int\"|\"bool\", \"default\": any}], \"steps\": [ ... ] (required, non-empty), \"outputs\": [{\"kind\": string}]}. A step is EITHER a tool call {\"tool\": <tool_name>, \"args\": {...}} OR a pause {\"wait\": {\"seconds\": N}} (also {\"wait\": N}). A tool-call step may add \"optional\": true (a boolean; default false): if it fails, recipe_run records {ok:false, error} for it, lists it in skipped_optional and carries on with the next step, instead of stopping — use it for a source the user may not have (an app, a permission). Any arg string may interpolate a declared param with ${name} (e.g. \"limit\": \"${count}\"); an arg that is exactly \"${name}\" keeps the param's type. Steps run in order; a later step can consume an EARLIER step's output with \"${steps[N].result.KEY}\" (0-based; supports .key and [i], e.g. \"account\": \"${steps[0].result.accounts[0].name}\"). A step cannot confirm itself: do not put `confirm` in a step's args (it is removed when saving and ignored when running). recipe_run asks for one confirmation when the recipe has state-changing steps; a step whose tool asks for its own confirmation then answers with its preview and is not executed. A step that names a note should give note_name (the note's title): a note_id only names the note on the Mac that issued it, so a step that gives both is saved with note_name alone."
    • addedInput schema / properties / overwrite
      Added value: +{
      +  "description": "Must be true to replace a recipe that already has this name. Without it, a taken name is refused with recipe_exists and the existing recipe is left as it was.",
      +  "type": "boolean"
      +}
  3. Changed1 schema field changed
    • changedInput schema / properties / manifest / description
      Previous value: -"The recipe manifest. Shape: {\"name\": string (required), \"description\": string, \"params\": [{\"name\": string, \"type\": \"string\"|\"int\"|\"bool\", \"default\": any}], \"steps\": [ ... ] (required, non-empty), \"outputs\": [{\"kind\": string}]}. A step is EITHER a tool call {\"tool\": <tool_name>, \"args\": {...}} OR a pause {\"wait\": {\"seconds\": N}} (also {\"wait\": N}). Any arg string may interpolate a declared param with ${name} (e.g. \"limit\": \"${count}\"); an arg that is exactly \"${name}\" keeps the param's type. Steps run in order; a later step can consume an EARLIER step's output with \"${steps[N].result.KEY}\" (0-based; supports .key and [i], e.g. \"account\": \"${steps[0].result.accounts[0].name}\"). On recipe_run a state-changing step previews first unless confirm:true."New value: +"The recipe manifest. Shape: {\"name\": string (required), \"description\": string, \"params\": [{\"name\": string, \"type\": \"string\"|\"int\"|\"bool\", \"default\": any}], \"steps\": [ ... ] (required, non-empty), \"outputs\": [{\"kind\": string}]}. A step is EITHER a tool call {\"tool\": <tool_name>, \"args\": {...}} OR a pause {\"wait\": {\"seconds\": N}} (also {\"wait\": N}). A tool-call step may add \"optional\": true (a boolean; default false): if it fails, recipe_run records {ok:false, error} for it, lists it in skipped_optional and carries on with the next step, instead of stopping — use it for a source the user may not have (an app, a permission). Any arg string may interpolate a declared param with ${name} (e.g. \"limit\": \"${count}\"); an arg that is exactly \"${name}\" keeps the param's type. Steps run in order; a later step can consume an EARLIER step's output with \"${steps[N].result.KEY}\" (0-based; supports .key and [i], e.g. \"account\": \"${steps[0].result.accounts[0].name}\"). On recipe_run a state-changing step previews first unless confirm:true."
  4. Changed1 schema field changed
    • changedInput schema / properties / manifest / description
      Previous value: -"The recipe manifest. Shape: {\"name\": string (required), \"description\": string, \"params\": [{\"name\": string, \"type\": \"string\"|\"int\"|\"bool\", \"default\": any}], \"steps\": [ ... ] (required, non-empty), \"outputs\": [{\"kind\": string}]}. A step is EITHER a tool call {\"tool\": <tool_name>, \"args\": {...}} OR a pause {\"wait\": {\"seconds\": N}} (also {\"wait\": N}). Any arg string may interpolate a declared param with ${name} (e.g. \"limit\": \"${count}\"); an arg that is exactly \"${name}\" keeps the param's type. Steps run in order; on recipe_run a state-changing step previews first unless confirm:true."New value: +"The recipe manifest. Shape: {\"name\": string (required), \"description\": string, \"params\": [{\"name\": string, \"type\": \"string\"|\"int\"|\"bool\", \"default\": any}], \"steps\": [ ... ] (required, non-empty), \"outputs\": [{\"kind\": string}]}. A step is EITHER a tool call {\"tool\": <tool_name>, \"args\": {...}} OR a pause {\"wait\": {\"seconds\": N}} (also {\"wait\": N}). Any arg string may interpolate a declared param with ${name} (e.g. \"limit\": \"${count}\"); an arg that is exactly \"${name}\" keeps the param's type. Steps run in order; a later step can consume an EARLIER step's output with \"${steps[N].result.KEY}\" (0-based; supports .key and [i], e.g. \"account\": \"${steps[0].result.accounts[0].name}\"). On recipe_run a state-changing step previews first unless confirm:true."
  5. Changed1 schema field changed
    • changedInput schema / properties / manifest / description
      Previous value: -"The recipe manifest."New value: +"The recipe manifest. Shape: {\"name\": string (required), \"description\": string, \"params\": [{\"name\": string, \"type\": \"string\"|\"int\"|\"bool\", \"default\": any}], \"steps\": [ ... ] (required, non-empty), \"outputs\": [{\"kind\": string}]}. A step is EITHER a tool call {\"tool\": <tool_name>, \"args\": {...}} OR a pause {\"wait\": {\"seconds\": N}} (also {\"wait\": N}). Any arg string may interpolate a declared param with ${name} (e.g. \"limit\": \"${count}\"); an arg that is exactly \"${name}\" keeps the param's type. Steps run in order; on recipe_run a state-changing step previews first unless confirm:true."
  6. Added

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare destructiveHint=true, readOnlyHint=false; the description earns those by explaining exactly what is destroyed (a same-named recipe is replaced only via overwrite:true, otherwise refused and nothing is written). It further discloses non-obvious behavior beyond the schema: `confirm` is stripped from step args and counted in confirm_removed_steps, and confirmation-requiring tools answer with previews at run time.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the definition and the primary use case before the edge cases. It is dense and mostly earns its length, though the confirm-removal rule is stated in the description and then restated at length in the schema, a mild duplication.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, yet the description discloses the return value ({name}) and the confirm_removed_steps signal, and covers the nested-manifest pitfalls (confirm stripping, note_name vs note_id, overwrite). For a two-parameter but structurally complex write tool, nothing an agent needs is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3; the description still adds meaning by restating the manifest contract (name plus non-empty steps) and clarifying overwrite's semantics as a replacement gate. The deep step/param interpolation semantics live mainly in the schema text, so this is reinforcement rather than new information.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a concrete verb and resource ("Saves a reusable SKILL — a named recipe ... of LMCP tool calls") and situates it against siblings by naming recipe_run and recipe_export explicitly. An agent can distinguish it from recipe_run/recipe_export/recipe_install without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives the selecting condition outright: save when a user repeats a multi-step workflow (morning brief, inbox triage, weekly report) and wants a single command; share via recipe_export; re-run via recipe_run. It also states the refusal path (recipe_exists) and the overwrite escape hatch, so the agent knows when to retry with a flag.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources