Inspiration
Everyday digital services assume too much. They assume you can read an SMS from your electricity board, parse an account number, understand what "surcharge" means, and act on it within a deadline. They assume you can log into a portal with a password you set two years ago, solve a CAPTCHA, and remember a reminder across weeks.
For millions of adults — especially older adults — that assumption creates a wall between what they want to accomplish and what the interface allows.
We didn't want to build a smarter chatbot. We wanted to build the layer that sits between a person and the services that assume too much. The user knows the outcome. The agent removes the complexity in between.
What it does
The agent runs a full loop: READ → UNDERSTAND → EXPLAIN → ANSWER → REMEMBER → PLAN → ACT → CONFIRM → COMMUNICATE
A user receives a confusing message — a bill, an appointment change, an event invitation, or a scam — and speaks to the agent. The agent:
- Extracts structured facts from the message (amount, due date, doctor name, location — the shape depends on the message type).
- Saves them as persistent context in DynamoDB.
- Explains the message in plain language.
- Answers follow-up questions only from the source. If the answer isn't there, it says so. It does not guess.
- Remembers the context across conversations — the user can say "that bill" days later and the agent resolves it.
- Resolves people by relationship — "my sister" becomes a real contact (Priya Sharma, WhatsApp, +91 98765 43210).
- Drafts a family message and stops.
- Shows a visible confirmation card with the exact recipient, channel, and message body.
- Sends only after the user explicitly says yes.
The confirmation gate is the product's centerpiece. The agent doesn't just ask — it enforces the ask across three independent layers.
How we built it
The system has three decoupled tiers: Web UI (React 19 + Vite 6) │ POST /api/chat ▼ Agent Orchestrator (Node.js + TypeScript) │ Intent classification, context resolution, safety policy │ MCP client over Streamable HTTP ▼ MCP Server (Node.js + TypeScript) │ 14 typed tools, Zod validation, guard enforcement ▼ DynamoDB (6 tables) + Amazon Bedrock (reasoning)
The agent and MCP server are separate processes. The agent decides what to do. The MCP server decides whether it's allowed. Neither trusts the other.
The MCP server is built against the Alexa+ MCP specification
2025-11-25 using StreamableHTTPServerTransport — the transport
Amazon requires, not the deprecated SSE-only variant.
The Fourteen MCP Tools
| Group | Tools |
|---|---|
| Understanding | analyze_message, scan_threat |
| Memory | get_context, save_context, list_recent_contexts |
| Scheduling | create_reminder, get_reminders, mark_reminder_fired, complete_reminder, find_fired_reminders, list_missed_reminders |
| Actions | resolve_contact, draft_family_message, send_family_message |
The Safety Model
External communication is governed by four independent layers. Each assumes the one above it might be compromised:
- Deterministic threat scanning — A regex dictionary runs before any LLM call. It detects urgency language, credential requests, payment coercion (gift cards, wire, crypto), suspicious short links, and IP-based URLs. Cannot be prompt-injected.
- Deterministic yes/no routing — When a confirmation is pending, a regex decides yes or no. The LLM is never consulted.
- Server-side send guards —
send_family_messagerefuses unless four conditions pass: draft exists, status is stilldraft, confirmation token matches viacrypto.timingSafeEqual, and the user confirmation is non-empty. - Database conditional writes — DynamoDB
ConditionExpressionenforcesdraft → confirmed → sent. No skipping. No double-sends.
A fabricated token fails. A reused token fails. A prompt injection inside a scam message is caught before the LLM ever sees it.
Amazon Services Used
| Service | Role |
|---|---|
| Amazon DynamoDB | Six tables for persistent state — users, contexts, reminders, drafts, contacts, conversations. GSIs for key lookup, TTL for expiry, conditional writes for the safety state machine. |
| Amazon Bedrock | Extraction and reasoning. The agent uses us.anthropic.claude-sonnet-4-6 via the Converse API with structured outputs. |
| Amazon EventBridge Scheduler | One schedule per reminder, created on demand, auto-deleted after firing. |
| AWS Lambda | Invoked by EventBridge, marks the reminder as fired, publishes to SNS. |
| Amazon SNS | Delivers the reminder to the user's email. |
| AWS CDK (TypeScript) | The whole reminder pipeline is defined as code. |
The reminder pipeline was verified end-to-end: a reminder created through chat was stored in DynamoDB, registered with EventBridge Scheduler, fired at the scheduled time, invoked the Lambda, and delivered an email via SNS within 90 seconds.
Challenges we ran into
1. The Bedrock model was retired mid-project. The default
anthropic.claude-3-5-sonnet-20241022-v2:0 reached end of life in
March 2026 and every call returned This model version has reached
the end of its life. We built a provider abstraction
(LlmProvider interface with Bedrock and OpenAI adapters) so the
agent could switch providers via one environment variable. This
turned a blocker into an architectural improvement.
2. Stateless MCP has a subtle gotcha. The SDK's stateful example
includes a guard that rejects any request without a session ID and
without an initialize body. That guard breaks every subsequent tool
call in stateless mode, because the server never issues a session ID.
Removing it was the single hardest debugging step in the project.
3. The Lambda had no compiled code. CDK's Code.fromAsset(path)
packages the directory verbatim. The Lambda runtime expects
index.js, and the directory contained index.ts. Every invocation
failed with Runtime.ImportModuleError. The fix was to add a compile
step and point CDK at dist/.
4. Two schedulers raced each other. A local interval-based
scheduler in the agent process and the AWS EventBridge Scheduler were
both trying to mark reminders as fired. Whichever won set firedAt,
and the other crashed on the conditional write — meaning the Lambda
never reached its SNS publish. Disabling the local scheduler when AWS
is configured eliminated the race entirely.
5. Timezone bugs in date parsing. "Remind me the day before" on
an appointment scheduled for 12 November produced a reminder for
10 November — a full day early. The bug was new Date("12 November")
parsing in local time (IST) and then manipulating in UTC, which
shifted the calendar day by one. Fixing it required constructing
dates explicitly in UTC.
6. The activity log returned yesterday's entries. The initial implementation filtered by "last 24 hours," which meant yesterday's test entries appeared alongside today's real activity. Changing the filter to "since midnight local time" fixed both the display and the semantics.
Accomplishments that we're proud of
The confirmation gate actually holds.
Most hackathon safety claims are prompt-level. Ours is architecture-level.
We built four independent enforcement layers — deterministic threat
scanning, regex-based yes/no routing, server-side token validation, and
DynamoDB conditional writes — and each one assumes the layer above it
might be compromised. A fabricated token fails. A reused draft fails.
A prompt injection inside a scam message is caught before the LLM ever
sees it. We have tests that assert send_family_message is never
called without user confirmation. The gate isn't a suggestion in a
system prompt. It's enforced on both sides of an HTTP boundary.
The MCP server is real, not a mock.
Fourteen typed tools over Streamable HTTP, spec 2025-11-25, built with
the official @modelcontextprotocol/sdk. The agent and the server are
separate processes — the agent decides what to do, the server decides
whether it's allowed. That separation is what makes the same code
deployable to Alexa+ later without changing a line.
The extraction is genuinely generic.
We tested it with a bill, a doctor's appointment, a birthday party invitation, and a phishing scam. Each produced a completely different entity shape with different fields. It's not hardcoded for bills. The extraction prompt handles bills, appointments, events, notices, deliveries, statements, and subscriptions with the same schema.
The AWS reminder pipeline works end-to-end.
A reminder created through chat is stored in DynamoDB, registered with EventBridge Scheduler, fires at the exact scheduled time, invokes a Lambda that marks it fired and publishes to SNS, and delivers a real email — verified in production, not just in code. The whole stack is defined as CDK code. This was the hardest piece to get working, and it's the strongest proof that the project is deployable.
Memory that actually persists.
The user can say "that bill I was worried about yesterday" in a brand new conversation with no context on screen, and the agent resolves it against DynamoDB. We verified this across conversation boundaries. The memory isn't in-process — it's in six DynamoDB tables with TTLs, GSIs, and conditional writes.
Contact resolution that names a real person.
"My sister" resolves to Priya Sharma, WhatsApp, +91 98765 43210, and the confirmation card shows the actual recipient before the user decides. When the relationship is ambiguous ("my relative"), the agent asks which one and remembers the question across turns.
Trust features that aren't bolted on.
Users can ask "What have you done today?" and get a voice-readable activity summary. They can say "Forget the electricity bill" and the row is hard-deleted from DynamoDB. The privacy model is documented — what's stored, for how long, and how to delete it.
We shipped a working product, not a demo.
86+ passing tests, a complete architecture blueprint, a privacy doc, a safety doc with a red-team table, a threat scanner evaluated against a 30-message labeled set, and a 3-minute demo video. Every claim in our submission is backed by code a judge can run.
What we learned
The safety model is architecture, not a prompt. Every guard that matters is deterministic and sits outside the LLM. The confirmation gate has to survive a compromised agent, a confused model, and a prompt injection — and the only way to guarantee that is to put the gate outside the model entirely.
Structured extraction beats vector RAG for typed data. A bill has
amount,currency,dueDate. Embeddings lose that structure. Exact key lookup is faster, cheaper, and more reliable for facts than semantic search.MCP is the right boundary. The agent reasons in domain terms (
resolve_contact,create_reminder). The MCP server owns the database keys, guard checks, and state transitions. The separation is what makes the same code deployable to Alexa+ later without changing a line.AWS's per-reminder scheduling pattern is the correct one. Polling a database every minute scales with total item count; EventBridge's one-schedule-per-reminder pays only when a reminder fires and auto-deletes after.
What's next for Everyday Independence Agent
- Alexa+ MCP Toolkit registration — the server is spec-compliant today. Toolkit access is partner-restricted, so the demo uses a simulated voice-first web UI connected to the same MCP server. Swapping to real Alexa+ is a registration step, not an architectural one.
- Amazon SES for email delivery — replacing the shared SNS subscription with SES removes the per-recipient confirmation step and enables multi-user delivery.
- Physical mail via camera — the extraction tool takes text. OCR in front of it is a natural extension.
- "Explain / Guide / Do" interaction modes — the pipeline already separates extraction from action, so a "guide me through it" mode is a prompt change, not an architecture change.
- Real message delivery — the draft lifecycle already stores the
resolved contact, channel, and address. Wiring in Twilio or the
WhatsApp Business API is under 20 lines of change inside
send_family_message.
Team
Power Puff Boys — Suraj & Kapish
Built for the Amazon Developer Hackathon 2026, Alexa+ Track.
Built With
- amazon-bedrock
- amazon-dynamodb
- amazon-eventbridge
- amazon-sns
- amazon-web-services
- amazone-cdk
- anthropic-claude
- aws-lamda
- css
- express.js
- git
- github
- html5
- javascript
- mcp
- mcp-sdk
- node.js
- npm
- openai
- react
- typescript
- vite
- web-audio-api
- web-speech-api
- zod


Log in or sign up for Devpost to join the conversation.