Structured agent ↔ user communication for AI coding agents: typed, validated forms instead of guessing or twenty questions.
Ask an agent for a security audit and it usually either guesses your intent or interrogates you one question at a time. Both failures share a root cause: free-form chat is the only channel most agents have. This library gives agents the other channel — a communication grammar of declarative, validated forms. Independent decisions batch into one round-trip; malformed questions are refused at build time; malformed answers are refused at collection time. Nothing is silently accepted in either direction.
The full argument: "A Communication Grammar for AI Agents".
As a Claude Code plugin (skill + MCP server, no Python setup):
claude plugin marketplace add Smart-AI-Memory/attune-forms
claude plugin install attune-forms@attune-formsThe plugin teaches the session the forms discipline (the forms skill)
and serves four MCP tools — elicitation_render_form,
elicitation_render_widget, elicitation_collect_response,
elicitation_ask — from this package via uvx. Decision cards,
pushback cards, progress forms, deliberation cards, triage boards,
confirm gates, ranking lists, and assumption reviews work out of the
box; rich HTML renders where the host
supports widgets, degrades to plain questions where it doesn't, and
renders as portable markdown on text-only hosts — with typed replies
parsed back into the same validator.
As a Python library:
pip install attune-formsPython 3.10+, one runtime dependency (structlog), 610+ tests, CI on Linux/macOS/Windows. Apache 2.0.
Beyond the plain field types (text, single/multi select, boolean, number, date, textarea), eight constructs carry conversational meaning:
- Decision — the agent proposes: recommended option first, a "why" rationale, a one-line tradeoff under every alternative. Validates exactly like a single-select; the enrichment is presentation.
- Pushback — structured disagreement: your stated approach appears as an option tagged "your approach", the agent's alternative is badged and ordered first, and overruling the agent is a first-class outcome, not a failure.
- Progress — a status report (done / in-flight / blocked) whose blocked items become a picker: reading the status and unblocking the work are the same gesture.
- Deliberation — several named voices (reviewers, models, teammates) endorse candidate positions; the endorsements render as chips so a 2-1 split is visible at a glance, the synthesis pick is a badge — never the answer — and the user chairs the choice.
- Triage — a ruling per item over a reviewed list (audit findings,
review comments): a shared disposition vocabulary, stable item ids,
and an answer that is the full
{item: disposition}mapping. - Confirm — an approval gate for consequential actions: the consequences are enumerated with severity tags, the answer is one of exactly two options, and nothing is ever pre-selected — a pre-checked approval would defeat the gate, so the validator forbids it.
- Ranking — the user orders the options, all of them or only the top N: the answer is the ordered list itself, a proposed order renders visibly as a proposal (never as the answer), and flat surfaces expand it to one pick per rank slot that folds back on collection.
- Assumption review — the agent lists the assumptions it inferred
from context (each with its source) and the user rules every one
accept/edit/reject, typing replacement text for an edit; the vocabulary is fixed,suggestedmay pre-mark accept only, and "infer first" stops being a discipline and becomes an artifact.
from attune_forms import form_from_dict, select_form_surface, form_to_widget_html
form = form_from_dict({
"title": "Security audit scope",
"fields": [
{"id": "path", "type": "text_input", "label": "Which path?"},
{"id": "depth", "type": "single_select", "label": "How deep?",
"options": ["quick", "standard", "thorough"]},
],
})
if select_form_surface(form) == "widget":
html = form_to_widget_html(form) # render on your widget surface- Renderers —
form_to_widget_html(self-contained interactive widget with postback),form_to_askuserquestion(batched payloads),form_to_elicitation_schema(native MCP elicitation), andform_to_markdown(portable markdown for text-only hosts, with a JSON answer skeleton as the reply format). - Typed-reply ingestion —
markdown_to_answersparses a pasted skeleton or line shorthand deterministically (unknown ids and stray lines become named problems, never guesses);problems_to_markdownre-asks exactly the fields that failed. - Surface routing —
select_form_surfacepicks widget vs fallback; a keyboard-mode opt-out is persisted per project. The form degrades — it never breaks. - Validation —
form_from_dictrefuses malformed definitions;collect_form_responserefuses malformed answers (required fields, option membership) with field-level problems. - Intake templates —
FormTemplate+FieldSlotgenerate a workflow's intake form at ask-time from named candidate providers (PROVIDERS): tools describe what they need once, and the form exists for free. - Telemetry — local-only surface-decision log, disabled via
DO_NOT_TRACK=1orATTUNE_FORMS_TELEMETRY=0. Nothing is ever phoned home.
Workflow-bound intake templates need two host hooks:
import attune_forms.intake_template as it
it.WORKFLOW_SCHEMA_RESOLVER = my_schema_resolver # name -> input schema
it.TEMPLATE_LOADERS.append(my_registration_loader) # imports template modulesExtracted from attune-ai's elicitation subsystem, where the grammar was designed and battle-tested; attune-ai now consumes this package. The grammar's own design decisions were made through its forms — including the review that killed one of its proposed features. See docs/communication-grammar-article.md (the verified master of the article) and CHANGELOG.md.
Apache 2.0. Copyright 2026 Smart AI Memory.