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:

  1. Extracts structured facts from the message (amount, due date, doctor name, location — the shape depends on the message type).
  2. Saves them as persistent context in DynamoDB.
  3. Explains the message in plain language.
  4. Answers follow-up questions only from the source. If the answer isn't there, it says so. It does not guess.
  5. Remembers the context across conversations — the user can say "that bill" days later and the agent resolves it.
  6. Resolves people by relationship — "my sister" becomes a real contact (Priya Sharma, WhatsApp, +91 98765 43210).
  7. Drafts a family message and stops.
  8. Shows a visible confirmation card with the exact recipient, channel, and message body.
  9. 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:

  1. 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.
  2. Deterministic yes/no routing — When a confirmation is pending, a regex decides yes or no. The LLM is never consulted.
  3. Server-side send guards — send_family_message refuses unless four conditions pass: draft exists, status is still draft, confirmation token matches via crypto.timingSafeEqual, and the user confirmation is non-empty.
  4. Database conditional writes — DynamoDB ConditionExpression enforces draft → 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

Share this project:

Updates

Submission history