Tools and the outside world
In plain language
A Solution that only writes text is safe but limited. A Solution that can act — send an email, fetch structured data, publish a result — has real consequences, so the platform treats tools differently from models: a tool call can be gated by a human decision, its recipients can be restricted to an allow-list, it is retried only with the same idempotency token, it never "falls back" to a different tool, and every invocation is on the record with a typed reason if it failed.
The mechanism
Tools are declared steps in the authored artifact. The composition
grammar's tool step (ADR-0015) names a platform tool, binds dynamic
inputs from capability inputs or prior steps, and carries static options.
The resolver compiles it to the substrate's tool node; the Studio write
path admits tool identities one governed tool at a time.
Review before tool (Gate A). At compile time the resolver checks that every path from entry to a tool step passes through a human-review step where the Solution's policy requires it; a definition that would reach a side effect without the required review is refused before it can be published. Reject routes away from the tool by default.
Recipient allow-lists. Email sends on both execution lanes
(run-workflow and dispatch-workflow-tool) are checked against a
configured recipient allow-list before the tool runs; an address outside
it is a refusal, not a send.
The tool contract (ADR-0013). Eight typed error codes —
TOOL_INPUT_INVALID, TOOL_OUTPUT_INVALID, TOOL_TIMEOUT,
TOOL_RATE_LIMITED, TOOL_UPSTREAM_UNAVAILABLE, TOOL_PERMANENT_FAILURE,
TOOL_PERMISSION_DENIED, TOOL_RETRY_REQUIRES_IDEMPOTENCY — each
classified retryable or not, with a catch-all that is not retryable by
default. Per-tool retry re-invokes the same tool with the same
idempotency token, capped at ten attempts and enforced both at validation
and at runtime. A tool that does not declare it honours idempotency is not
retried unless the author explicitly tolerates it. Every invocation writes a
ToolInvocation ledger row carrying its error class.
No tool fallback chain. Falling over from send_email to
send_slack_message is a semantic change, not a routing decision; tools
have a different blast radius from models.
The tools that exist. tool-send-email (SES, allow-listed,
review-gated), tool-structured-data-fetch, and tool-workflow-validator
— each a separate function with its own least-privilege role, invoked by
ARN from the workflow executor.
Proven live. One governed email send has run end-to-end in production through the authored path: review gate, allow-list, tool invocation truth with deduplication, and tool cost attributed on the run.
Outward from the platform to a client. Solution webhooks are signed and delivered at least once with retry into a dead-letter queue (see The API). Inbound webhooks (for example billing events) are signature-verified before any processing, idempotent on the provider's event id, and processed asynchronously on the job runtime.
Design notes
- Why tools are not models (ADR-0013). "They have real-world side effects and must be treated accordingly." One error code for everything had made "was this transient, should it have been retried" unanswerable from the audit row; documented-but-unenforced idempotency had let a non-honouring tool duplicate side effects silently on retry.
- Why the grammar came after the runtime (ADR-0015). The engine had been tool-capable, but no authored Solution could reach it; rather than let a tool identity silently execute as a prompt, the write path refused it until the grammar existed.
Sources
magpie/docs/adr/0013-tool-contract-formalization.md.mag-composition/docs/adr/0015-tool-step-grammar.md.matterandgas-com/amplify/resolver/compile.ts(Gate A),amplify/custom/workflow-email-recipient-allowlist.ts,amplify/functions/tool-send-email/,dispatch-workflow-tool/.matterandgas-com/docs/runbooks/tools.md,human-review.md.