attune-forms
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".
What's new in 0.14.0
- Renderer registry and no-escape sweep —
RENDERER_REGISTRYis the public inventory of every production projection of a form or workspace view (RICH, PORTABLE, HEADLESS, and the legacy AskUserQuestion renderer as a compatibility-only host-native target with a pinned contract id and shape digest).sweep_production_renderers()proves no projection-shaped callable escapes it; unresolved annotations fail closed. - Production HEADLESS workspace projection —
workspace_to_headless(view, binding)returns one deterministic JSON-safe mapping: the complete view, the full form schema when present, the binding, and the response contract a host posts back throughcollect_workspace_action. Never an action-id list. - Digests and shipped fixtures —
record_digest,registry_digest,implementation_digestandcanonical_fixtureslet a consuming gate lock a released artifact and re-execute every record's fixture from the wheel.
This minor adds the registry line (attune-ai host-surface-parity AF-1); everything from 0.13 remains available unchanged.
What's new in 0.13.0
-
Templates cast server-side — every form-taking MCP tool (
elicitation_render_form,elicitation_render_widget,elicitation_collect_response,elicitation_ask) takestemplate+slotsin place ofform. The server loads the stored template, fills the slots, validates, and renders in one call, so the form definition never transits the agent's context. Template-cast collections carry the template name astemplate_id. -
Authoring preview —
python -m attune_forms.preview --open(orattune-forms-preview) renders every stored template through the production widget renderer into one standalone page, light and dark, with the posted payload shown on submit. Edit a template, reload, see what users see. Preview casts never count toward form telemetry. -
Cast-every-template gate — stored templates carry
example_slots, and a drift test casts each one and validates the result, so no template ships uncastable. -
Correlated display and workspace acceptance telemetry (0.12.3) — each display carries its own instance token through submission; separate render/acceptance events let hosts measure validated transitions.
-
Visible consequential submits and action-scoped workspace responses (0.12.x) — inline two-click confirmation, one action collecting a validated response mapping across widget, Markdown, headless, and MCP stdio surfaces.
-
Interaction conformance evidence — the packaged harness checks structural DOM, keyboard traversal, constrained viewports, projection parity, submitted state, and separately attributed cold/warm latency phases.
This minor adds the template-bound form path; everything from the 0.12 line remains available unchanged.
This is the provider-neutral transport layer. Individual agent products still choose whether to render it inline, open it as a browser artifact, or use the fallback that matches their advertised capabilities.
Install
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-forms
The plugin teaches the session the forms discipline (the forms skill)
and serves six MCP tools — elicitation_render_form,
elicitation_render_widget, elicitation_collect_response,
elicitation_ask, elicitation_render_workspace, and
elicitation_collect_workspace_action — from this package via uvx. Every
form-taking tool also takes template + slots: a stored template is cast,
validated, and rendered server-side in one call, so the form never transits
the agent's context. Decision cards,
pushback cards, progress forms, deliberation cards, triage boards,
confirm gates, ranking lists, and assumption reviews work out of the
box. MCP Apps hosts discover one shared ui:// resource, render the
rich surface inline, send user actions through the same server-side
validator, and return the validated result to the conversation. Other
hosts degrade to plain questions where possible and render as portable
markdown on text-only hosts — with typed replies
parsed back into the same validator.
In Codex (CLI or the desktop app) — register the same MCP server, then install the skill so the session learns the forms discipline:
codex mcp add attune-forms -- uvx --from 'attune-forms[mcp]' attune-forms-mcp
mkdir -p ~/.codex/skills/forms && curl -fsSL https://raw.githubusercontent.com/Smart-AI-Memory/attune-forms/main/plugin/skills/forms/SKILL.md -o ~/.codex/skills/forms/SKILL.md
In any other MCP client (Antigravity, Gemini CLI, Cursor, …) — add
the server to the client's MCP config. This JSON is the plugin's own
.mcp.json, verbatim:
{"mcpServers": {"attune-forms": {"command": "uvx", "args": ["--from", "attune-forms[mcp]", "attune-forms-mcp"]}}}
Agents that read the .agents/skills/ convention (Antigravity, Codex
inside a checkout) find the same skill at .agents/skills/forms/SKILL.md
— a byte-for-byte mirror of the plugin skill, drift-guarded. Every host
gets the same six tools and the same validator; only the surface tier
differs (rich ui:// where the host renders MCP Apps, plain questions or
portable markdown elsewhere).
As a Python library:
pip install attune-forms
Python 3.10+, one runtime dependency (structlog), 950+ tests, CI on Linux/macOS/Windows. Apache 2.0.
The grammar
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.
Quick start
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
One schema, every surface
- MCP Apps transport — capable hosts advertise
io.modelcontextprotocol/ui, receive UI metadata only after that negotiation, and render the sharedui://attune-forms/dynamic-surface/v1resource. App submissions call the existing collector tools; only a successful validated result is offered back to model context. Hosts missing app-to-server or app-to-chat capabilities show an explicit manual-continuation state rather than a dead control. - 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. Authority note: in the shipped plugin the router is advisory — the agent's choice of MCP tool IS the surface decision, guided by the skill's prose ladder, and the router runs after the fact so telemetry can record agreement. Library consumers routing their own calls (as above) are the path where its answer is binding. - Validation —
form_from_dictrefuses malformed definitions;collect_form_responserefuses malformed answers (required fields, option membership) with field-level problems. - Command workspaces —
workspace_from_dictvalidates a closed, non-executable document grammar for intake, preview, execution, and receipt views.workspace_to_widget_htmlandworkspace_to_markdownrender the same action contract;collect_workspace_actionrejects unknown, stale, or mismatched action envelopes. An action may declareresponse_fieldsfrom the existing form grammar; only the selected action's fields are accepted, and every surface returns the same normalizedresponsesmapping.workspace_action_contractprovides the immutable projection hosts bind into their contract digest. Optional revision/hash/nonce bindings are echoed for the host to authorize and consume — the UI never grants authority. - Authoring preview —
python -m attune_forms.preview --openrenders every stored template (cast with itsexample_slots) through the production widget renderer into one standalone page, light and dark, with the posted payload shown on submit. Edit a template, reload, see what users see. - 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.
Host integration seams
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 modules
Renderer registry
RENDERER_REGISTRY names every production projection of a form or a
workspace view — RICH, PORTABLE, HEADLESS, and any host-native target —
and sweep_production_renderers() proves nothing escapes it: every
callable that takes a FormSchema or WorkspaceView and returns one of
the closed projection_output_types must be exactly one registry
target, exported, or on the small rationale-bearing allowlist.
workspace_to_headless(view, binding) is the workspace family's
production HEADLESS projection: the complete view plus the response
contract a host posts back through collect_workspace_action.
record_digest, registry_digest and implementation_digest let a
consuming gate lock a released artifact; canonical_fixtures ships the
fixtures every record executes.
Interaction conformance
run_workspace_conformance evaluates a command-neutral WorkspaceFixture
against one of four capability profiles: RICH_WIDGET_STANDARD,
NATIVE_DIALOG_CONSTRAINED, PORTABLE_MARKDOWN, or HEADLESS_JSON.
The report checks the rendered structure and complete action set rather than
screenshots or label substrings:
from attune_forms import (
RICH_WIDGET_STANDARD,
ProjectionRenderers,
WorkspaceFixture,
run_workspace_conformance,
)
fixture = WorkspaceFixture(
owner="my-workflow",
pages=(workspace_view,),
expected_action_ids=tuple(action.id for action in workspace_view.actions),
submitted_summary="The review is complete.",
)
report = run_workspace_conformance(
fixture,
RICH_WIDGET_STANDARD,
renderers=ProjectionRenderers(retained=capture_submitted_projection),
latency_samples=observed_phase_samples,
)
The retained callback captures the host's actual compacted submitted
projection. The fixture's expected summary alone cannot pass retention.
Latency samples name the cold/warm mode and the exact phase they measure.
measure_latency can capture local operations; transport, acknowledgement,
progress, and terminal phases must come from those real boundaries. A missing
phase or an explicitly unavailable receipt remains non-passing. Profiles and
reports describe evidence only—they cannot authorize a workspace action.
Provenance
Extracted 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.
License
Apache 2.0. Copyright 2026 Smart AI Memory.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file attune_forms-0.14.0.tar.gz.
File metadata
- Download URL: attune_forms-0.14.0.tar.gz
- Upload date:
- Size: 287.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0741d987f16f1e9a0d06f603a5bcd8446ac373e420b8385d8d4b815ff5a4021d
|
|
| MD5 |
5fa273279f67aa276285943ae299275f
|
|
| BLAKE2b-256 |
21bd5084cd7cfc76e6a79dd447f56662dbfbee047df80d67ec80227426014c64
|
Provenance
The following attestation bundles were made for attune_forms-0.14.0.tar.gz:
Publisher:
publish.yml on Smart-AI-Memory/attune-forms
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
attune_forms-0.14.0.tar.gz -
Subject digest:
0741d987f16f1e9a0d06f603a5bcd8446ac373e420b8385d8d4b815ff5a4021d - Sigstore transparency entry: 2733379953
- Sigstore integration time:
-
Permalink:
Smart-AI-Memory/attune-forms@5600afe0d82b3d08c3e4becb909a7c6a0762379f -
Branch / Tag:
refs/tags/v0.14.0 - Owner: https://github.com/Smart-AI-Memory
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5600afe0d82b3d08c3e4becb909a7c6a0762379f -
Trigger Event:
push
-
Statement type:
File details
Details for the file attune_forms-0.14.0-py3-none-any.whl.
File metadata
- Download URL: attune_forms-0.14.0-py3-none-any.whl
- Upload date:
- Size: 161.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
43e377b94b21f3d7c22500963916bd80b24c81afb7f578b73602387584001cec
|
|
| MD5 |
ea1b385049d1aec79ccf1ead46104ba0
|
|
| BLAKE2b-256 |
bf7050a2591524413891a9229f4a306015f5d5444f898e0a82ae75374c5a35b1
|
Provenance
The following attestation bundles were made for attune_forms-0.14.0-py3-none-any.whl:
Publisher:
publish.yml on Smart-AI-Memory/attune-forms
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
attune_forms-0.14.0-py3-none-any.whl -
Subject digest:
43e377b94b21f3d7c22500963916bd80b24c81afb7f578b73602387584001cec - Sigstore transparency entry: 2733380017
- Sigstore integration time:
-
Permalink:
Smart-AI-Memory/attune-forms@5600afe0d82b3d08c3e4becb909a7c6a0762379f -
Branch / Tag:
refs/tags/v0.14.0 - Owner: https://github.com/Smart-AI-Memory
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5600afe0d82b3d08c3e4becb909a7c6a0762379f -
Trigger Event:
push
-
Statement type: