You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Increasingly, the one registering Appduct tools in an app is itself an agent (the README already says "asking an agent to add Appduct to your app or write its tools? Install the skill"). The skill and docs/TOOLS.md explain the mechanics thoroughly: schema forms, object-rooted roots, timeoutMs, per-mount registration. They say nothing about what makes a tool good for the agent that will call it, so an agent asked to "expose the checkout flow" will happily produce doCheckout(json: object) with no annotations, no output schema and a one-word description. Everything downstream (tools/list in an MCP client, the signature listing from #67, the policy engine keyed on destructiveHint) then degrades.
Tool quality is what a calling agent actually experiences. This is documentation only, and it multiplies the value of every other agent-facing feature.
Proposal
Add a "Designing tools for agents" section to skills/appduct/SKILL.md (the copy an agent reads), with a matching section in docs/TOOLS.md and a pointer from the React Native, iOS and Android READMEs. Each rule gets a one-line reason and a before/after example:
Name by intent, not implementation.seed_cart, go_to_checkout, set_feature_flag; never dispatch_action or run_effect.
Always set annotations.readOnlyHint on every observer; destructiveHint on anything that deletes, logs out, resets or pays. The daemon's policy engine and the MCP client's permission UI key on these.
Pair every mutation with an observer. An agent cannot verify seed_cart without get_cart. Return the resulting state from the mutation itself where it is cheap ({ cartId, items }), so the common case is one call.
Declare an outputSchema. It is what makes the result structuredContent over MCP and lets the signature listing show -> { ... }. Wrap non-object results in an object.
Describe preconditions and side effects. "Requires a signed-in user. Navigates to the Cart tab." The first line is what the listing shows; keep it one sentence.
Describe every parameter (.describe() in zod, description in raw JSON Schema), with units and allowed values; prefer enums over free strings.
Make setup tools idempotent (login(userId) is a no-op when already signed in as that user) and say so with idempotentHint.
Prefer a few coarse tools over many fine ones.complete_onboarding() beats dismiss_step_1..7. An agent loses more to a long tool list than to a slightly wider tool.
Declare timeoutMs on anything that touches the network; the default is 10 s.
Do not register tools that need UI cooperation to finish (a tool that opens a modal and resolves when the user taps). Return once the state change is done, or post an app_event for the rest.
Keep both schemas object-rooted (already documented; link to it).
For the native SDKs: the same rules, phrased for AppductToolDescriptor / registerTool in Swift and Kotlin.
Also add one line to the skill's setup flow: after registering tools, run appduct tools and read the listing back as the calling agent will see it; fix any ..., missing -> { }, or description that does not say what the tool needs.
Acceptance criteria
The skill and docs/TOOLS.md carry the section; each rule has an example; the native READMEs link to it.
The playground's tools follow the rules (they are the reference an agent copies). Adjust descriptions and annotations there where they do not.
docs/CI.md's markdown link check passes.
Out of scope
Any automated checking of these rules, in the CLI or in the SDKs. Descriptions and annotations are judgement calls; the guidance is the deliverable.
Why
Increasingly, the one registering Appduct tools in an app is itself an agent (the README already says "asking an agent to add Appduct to your app or write its tools? Install the skill"). The skill and
docs/TOOLS.mdexplain the mechanics thoroughly: schema forms, object-rooted roots,timeoutMs, per-mount registration. They say nothing about what makes a tool good for the agent that will call it, so an agent asked to "expose the checkout flow" will happily producedoCheckout(json: object)with no annotations, no output schema and a one-word description. Everything downstream (tools/listin an MCP client, the signature listing from #67, the policy engine keyed ondestructiveHint) then degrades.Tool quality is what a calling agent actually experiences. This is documentation only, and it multiplies the value of every other agent-facing feature.
Proposal
Add a "Designing tools for agents" section to
skills/appduct/SKILL.md(the copy an agent reads), with a matching section indocs/TOOLS.mdand a pointer from the React Native, iOS and Android READMEs. Each rule gets a one-line reason and a before/after example:seed_cart,go_to_checkout,set_feature_flag; neverdispatch_actionorrun_effect.annotations.readOnlyHinton every observer;destructiveHinton anything that deletes, logs out, resets or pays. The daemon's policy engine and the MCP client's permission UI key on these.seed_cartwithoutget_cart. Return the resulting state from the mutation itself where it is cheap ({ cartId, items }), so the common case is one call.outputSchema. It is what makes the resultstructuredContentover MCP and lets the signature listing show-> { ... }. Wrap non-object results in an object..describe()in zod,descriptionin raw JSON Schema), with units and allowed values; prefer enums over free strings.login(userId)is a no-op when already signed in as that user) and say so withidempotentHint.complete_onboarding()beatsdismiss_step_1..7. An agent loses more to a long tool list than to a slightly wider tool.timeoutMson anything that touches the network; the default is 10 s.app_eventfor the rest.--filter(feat(cli): agent-friendly output — signature tools listing with filter/paging, table-driven global flags, --pretty/--verbose, compact JSON #67) finds the tool, and use groups once they exist (Tool groups: optionalgroupon the descriptor,--groupontools.list, and lazy group exposure over MCP #70).AppductToolDescriptor/registerToolin Swift and Kotlin.Also add one line to the skill's setup flow: after registering tools, run
appduct toolsand read the listing back as the calling agent will see it; fix any..., missing-> { }, or description that does not say what the tool needs.Acceptance criteria
docs/TOOLS.mdcarry the section; each rule has an example; the native READMEs link to it.docs/CI.md's markdown link check passes.Out of scope