Aura
README.md
# Aura — the household agent that remembers
**Build, Ship, Shape: Amazon Developer Hackathon · Alexa+ track · + Open Source mini-challenge**
> The official rules ask Alexa+ judges to separate *creative* from *obvious*, and name what
> counts as creative: "agentic workflow that orchestrates across services autonomously,
> context-aware add-on that maintains state across sessions, purchasing capabilities, media
> support (cards, carousels, etc), MCP Apps, Agent Skills." **All six are implemented here**, and
> [`docs/JUDGING_MAP.md`](docs/JUDGING_MAP.md) points each one at a file and at the `npm run
> verify` check that fails if it is removed. The two patterns the rules call *obvious* — a
> single-turn Q&A bot, and a basic MCP wrapper around one existing API — are what this is not.
A self-hosted **MCP server** (Streamable HTTP, protocol `2025-11-25`) plus an **Agent Skill**
that gives an Alexa+ style assistant three things it does not have out of the box:
1. **Cross-session household memory** — who lives here, what they're allergic to, the spend
cap, the screen-time curfew, quiet hours, and what the household decided last night.
2. **Autonomous re-planning** — the agent starts with a plan, then changes it when an
observation contradicts it, and says *why* in a visible timeline.
3. **A card and carousel layer** — `ui://` MCP App templates so the spoken answer stays short
while the screen carries the detail, plus a purchase step that refuses to spend without
an explicit spoken confirmation.
Say *"set up a movie night for everyone"* and Aura checks the weather, discovers a 74 % chance
of rain, swaps the backyard idea for an indoor film, filters out the PG-13 result because a
child is home, drops the priciest snack when the basket blows the ₹2 100 budget, asks before
spending above the cap, and commits the night to memory so tomorrow's *"what did we watch?"*
resolves without a single follow-up question.
```
"Alexa, set up a movie night for everyone"
✓ household_profile Load household rules, members and budget 3 members, budget 25
✓ weather_now Indoors or outdoors decides the plan Pune: 24.1°, light rain
✓ media_search Candidate titles 6 result(s)
✓ pantry_status Snacks already in stock 4 low, allergens peanuts/dairy
+ media_search RE-PLAN Rain risk 74% → indoor-friendly film
+ learn_fact RE-PLAN Add a rainy-evening card
✓ place_order Purchase guardrail → needs_confirmation
✓ commit_routine Saved: tomorrow "what did we watch?" just works
```
## Run it
Node 20+ (no API keys, no accounts, no hardware).
```bash
npm install
npm start # http://localhost:8787
```
Open <http://localhost:8787> — that page is the **simulated Alexa+ device** the rules allow:
a microphone button (Web Speech API), suggestion chips, the card/carousel surface, the live
agent timeline, and the household memory inspector.
The device bar also has a **household clock** selector (live / 23:05 after the curfew / 07:05). It only
sets the `hour` the planner reasons about — the same value the `curl` example below already accepts — so
the time-dependent re-planning is reproducible whatever weather and timezone the judge's machine has.
Amazon does not ship a local host simulator for this surface, so building the harness was part
of the work, not a shortcut around it: the page is a real `Client` +
`StreamableHTTPClientTransport` that reads the `ui://` templates back *through MCP*, so every
step between the two halves is exercised rather than stubbed. That gap is logged as feature
request #1 and friction F1 in [`FRICTION_LOG.md`](FRICTION_LOG.md).
Two other ways to see it work:
```bash
npm run demo # headless CLI: 4 spoken turns, ASCII cards, full trace, audit log
npm run verify # 19 acceptance checks (protocol, MCP Apps mime, guardrails, memory, decision audit, skill contract)
npm run validate:skills # check SKILL.md frontmatter against what the server actually registers
```
Deterministic demo data (recommended for recording, because it never shows third-party
catalogue titles):
```bash
AURA_OFFLINE=1 npm start
```
Force the clock to show curfew and quiet-hours behaviour on demand:
```bash
curl -s localhost:8787/api/agent -H 'content-type: application/json' \
-d '{"utterance":"good morning","session":"kitchen","hour":23}'
```
## Point a real MCP client at it
```bash
npx @modelcontextprotocol/inspector --cli http://localhost:8787/mcp --method tools/list
```
Claude Desktop / Cursor / Kiro config:
```json
{ "mcpServers": { "aura": { "url": "http://localhost:8787/mcp" } } }
```
The Agent Skill is served *by the server* as `skill://aura-household/SKILL.md` and as the
`load_agent_skill` tool, so any agent that connects picks up Aura's planning policy without
reading this repository. To install it as a local skill instead, copy `skills/aura-household/`
into `.kiro/skills/`, `.claude/skills/` or `.github/skills/`.
## How it is built
```
src/server.js McpServer + Streamable HTTP (stateless), 14 tools, 6 resources, 3 prompts,
/api/agent (simulated device), /api/resource (MCP Apps host), bearer-token option
src/agent.js classify -> plan -> act -> observe -> re-plan -> speak
src/memory.js household profile, sessions, audit log, spend cap, allergen vetoes
src/tools.js one implementation shared by the MCP surface and the agent
src/services/ open-meteo (weather), iTunes Search (catalogue), HN Algolia (headlines),
Wikipedia REST (explainer card), open.er-api (household currency)
src/ui/ index.html + app.js: the simulated Alexa+ device; templates.js: ui:// resources
skills/aura-household/SKILL.md the Agent Skill (+ references/ for progressive disclosure)
scripts/demo.mjs headless end-to-end run tests/verify.mjs acceptance checks
```
Every tool result returns `content[]` (for voice-only clients), `structuredContent`
(for data-only rendering) and `_meta.ui.resourceUri` (to bind the result to a `ui://`
template) — the three ways an MCP host can present the same answer.
**Why stateless?** Alexa+ owns the conversation and replays context from its own history, so a
per-connection session would fight it. Household state therefore lives in `data/household.json`,
not in the transport.
**Why no LLM in the loop?** The planner is a policy engine, so judges can run it offline with
zero keys and still see genuine plan/observe/re-plan behaviour. `src/agent.js` documents the
two functions (`classify`, `replan`) you swap out to drive the same tools from Bedrock or any
tool-calling model — the tool surface, guardrails and memory are unchanged.
## Testing instructions for judges
| What | How | Expected |
| --- | --- | --- |
| Protocol + transport | `npm run verify` check 1, or `curl localhost:8787/health` | `protocol: 2025-11-25` over Streamable HTTP |
| MCP Apps cards | Ask *"set up a movie night"* in the web app | right-hand carousel renders inside a sandboxed frame from `ui://aura/cards` |
| Autonomous re-plan | set **household clock = 23:05** in the device bar, then ask *"set up a movie night"* | `ADDED-BY-AGENT` row (audio instead of video) and a `SKIPPED` `place_order` — reproducible on any machine, in any weather |
| Autonomous re-plan, weather-driven | `AURA_OFFLINE=1 npm start`, ask *"set up a movie night"* | wet-evening fixture triggers the indoor-swap re-plan |
| Spend guardrail | `npm run verify` checks 7–8, or ask *"order snacks"* | `needs_confirmation` above the cap; nothing bought until `confirmed: true` |
| Allergen memory | ask *"we are out of chai, order snacks"* | peanut brittle and yogurt vetoed *by name of the member they're unsafe for* |
| Cross-session memory | movie night in `living-room`, then *"what did we watch last night?"* in `bedroom` | answers from carried memory, no questions back |
| Curfew / quiet hours | `curl` the `hour: 23` example above | headlines skipped, calm card substituted, video converted to audio |
| Decision is explainable, not just claimed | say *"why did that change?"*, call `describe_last_decision`, or `npm run verify` check 17 | returns the routine's persisted trace: tools in execution order, every `added-by-agent` step with the planner's own reason text, every `skipped` step, and whether the spend cap blocked a purchase. `traceAvailable: false` when no trace was stored — it never guesses. Check 19 proves the voice path reaches it, and the demo page renders it in a third `ui://aura/decision` frame |
| Agent Skill contract is enforced, not assumed | `npm run validate:skills`, or `npm run verify` check 18 | 7 checks pass against the real `skills/aura-household/SKILL.md`; pointed at a deliberately broken fixture it exits 1 and names the offending key (`verify` check 18 does exactly that, with a temp directory it cleans up) |
`npm run verify` prints `19/19 checks passed` and exits non-zero on any regression.
[`docs/RUN_LOG_2026-10-23C.md`](docs/RUN_LOG_2026-10-23C.md) is the current run — a clean clone of commit
`d346586`, install, `validate:skills`, verify, demo, the explain voice path driven over HTTP, the MCP
surface read by a real client, and the `ui://aura/decision` frame filled in a browser (with the real output
of each).
[`docs/RUN_LOG_2026-10-23B.md`](docs/RUN_LOG_2026-10-23B.md) records the prior clean clone at `6cb7293` (18 checks);
[`docs/RUN_LOG_2026-10-23.md`](docs/RUN_LOG_2026-10-23.md) the one at `99ea087` (17 checks, and a real MCP `callTool` of
`describe_last_decision`);
[`docs/RUN_LOG_2026-10-22.md`](docs/RUN_LOG_2026-10-22.md) is the earliest run, at commit `37c3104`.
[`docs/JUDGING_MAP.md`](docs/JUDGING_MAP.md) maps each judging criterion to the files and the
check that evidences it.
## Hackathon compliance
| Requirement | Where |
| --- | --- |
| Self-hosted MCP server, spec ≥ `2025-11-25`, Streamable HTTP | `src/server.js`; asserted by `tests/verify.mjs` |
| Agent Skill | `skills/aura-household/SKILL.md`, also served as `skill://` + `load_agent_skill` |
| Track technology used **at runtime** in code, not just README | the simulated device is a real `StreamableHTTPClientTransport` client; `ui://` resources are read over MCP to render the panel |
| Simulated Alexa+ experience in a web app | `src/ui/index.html` (allowed alternate path) |
| Media support: cards, carousels | `_meta.ui.resourceUri` → `ui://aura/cards`, `ui://aura/plan`, `ui://aura/decision` |
| Purchasing capability | `place_order` behind a spend cap + explicit confirmation |
| Context-aware, state across sessions | `src/memory.js`, `commit_routine`, `household_audit` |
| Public repo with an open-source licence | this repository, `LICENSE` (Apache-2.0) |
| Clear setup and run instructions | this file |
| Open Source mini-challenge | see `docs/OPEN_SOURCE.md` (companion repo + contribution) |
| AWS Builder mini-challenge (built with Kiro) | see `docs/KIRO_TASK.md` — `describe_last_decision`, its `ui://aura/decision` template and verify check 17, plus `scripts/validate-skills.mjs` and check 18, came out of Kiro CLI 2.33.1 agent sessions run against this repo. No AWS service runs inside Aura; Kiro was the build tool, and that distinction is stated in the submission |
| Product feedback / friction logs / feature requests | `PRODUCT_FEEDBACK.md`, `FRICTION_LOG.md`, `FEATURE_REQUESTS.md` |
## Built with Kiro
`describe_last_decision` — the tool, its `ui://aura/decision` template and verify check 17 — and
`scripts/validate-skills.mjs` with verify check 18 were written with **Kiro CLI 2.33.1**, not by hand.
The verbatim prompts are in
[`docs/KIRO_TASK_PROMPT.txt`](docs/KIRO_TASK_PROMPT.txt),
[`docs/KIRO_TASK_PROMPT_2.txt`](docs/KIRO_TASK_PROMPT_2.txt) and
[`docs/KIRO_TASK_PROMPT_3.txt`](docs/KIRO_TASK_PROMPT_3.txt); what each session returned is recorded in
[`docs/KIRO_TASK.md`](docs/KIRO_TASK.md).
The sequence is worth stating because the first result was not good enough:
1. **Session 1** added the tool, template and a check; `npm run verify` went to 16/16. But two fields were
inference dressed as fact: `addedByAgent` regex-matched the summary for words like `rain|curfew|quiet`,
and `skippedStep` was hardcoded `null`.
2. **Session 2** was told to make those exact — persist the real trace when a routine commits, read from it,
delete the heuristic. Kiro diagnosed its own failing check 17: the `commit_routine` Zod schema silently
stripped the `trace` field before the handler ever saw it. It fixed the schema, threaded the trace through
`src/agent.js` → `src/memory.js`, removed the guesswork, and returned `traceAvailable: false` for legacy
data instead of inventing an answer. Suite went to 17/17.
3. **Session 3** was given feature request #8 — a skill validator, because a typo in `SKILL.md` frontmatter
is otherwise discovered only by the agent quietly ignoring the skill. Kiro read `src/server.js`, derived
the contract from the code that registers the `skill://` resource, wrote a dependency-free frontmatter
parser and added check 18, which fails a deliberately broken fixture in a temp directory and asserts the
validator exits 1 naming the exact key. Suite went to 18/18. I then removed ~50 lines of dead duplicated
parser code it had left behind and re-ran everything.
4. **Checked by hand afterwards:** `npm run verify`, `npm run validate:skills`, `npm run demo`, a clean
re-clone, and a line-by-line read of the diff. Kiro ran no git command (forbidden in the prompt) — every
commit here is authored by me.
5. **The `explain` intent is mine, not Kiro's.** Kiro built the `describe_last_decision` tool, but the
flagship "explainable decision" claim was unreachable from the voice path: nothing routed a spoken
"why did that change?" to it, and `ui://aura/decision` was served over MCP yet never rendered. I added
the intent, the plan step, the spoken summary branch, the third decision frame in the demo UI, and
`verify` check 19 that proves the whole path over MCP. Suite is now 19/19.
One limitation of this evidence, stated rather than hidden: the CLI session authenticated with the **GitHub
social identity**, so the credit meter on the Builder ID account at `app.kiro.dev` does not show these
sessions' usage.
## Third-party services
All free and key-less: Open-Meteo (weather/geocoding), iTunes Search API (catalogue),
Hacker News Algolia (headlines), Wikipedia REST (explainer), open.er-api.com (currency).
Artwork is generated locally as SVG (`/art/:title.svg`) so the demo never shows third-party
posters — set `AURA_ALLOW_REMOTE_ART=1` to use catalogue art instead. The household, its
members and every media title in the fixtures are invented for the demo.
## Feedback, licence, author
Read [`PRODUCT_FEEDBACK.md`](PRODUCT_FEEDBACK.md) for what worked and what did not while
building against the Alexa+ MCP toolkit docs, [`FRICTION_LOG.md`](FRICTION_LOG.md) for the
seven entries with severity and workarounds, and [`FEATURE_REQUESTS.md`](FEATURE_REQUESTS.md)
for the asks ranked Critical/Important/Nice-to-have.
Apache-2.0 · Abhinav Shukla · Pune, India · October 2026
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues