Inspiration

In January and February 2025, Nelson Mandela Bay logged 958 streetlight complaints by phone. Only 43 were attended to, and 3,787 callers hung up while waiting (The Herald, 19 Mar 2025). When a geyser bursts or the street goes dark, a South African household is juggling three jobs at once: what to do right now, getting the municipality to fix the public part, and finding a plumber it can trust for the private part. Each job means a separate call, app or website, usually done with wet hands in the kitchen. Voice suits that moment, so we built Fixwise to turn one spoken sentence into all three jobs, with nothing booked until the household says yes.

What it does

Fixwise is a self-hosted MCP server (spec 2025-11-25, Streamable HTTP) that Alexa+ calls as an add-on. We demo it through a simulated Alexa+ console, which is the path the rules allow. Say "Water's pouring from the geyser, and there's a pothole on 4th Avenue" and Fixwise does five things:

  1. Safety first. It reads out shut-off steps, and emergency numbers when someone may be in danger. It never replaces 112, 10177 or 10111.
  2. Splits compound reports. One sentence with several faults becomes separate repairs. A geyser leaking through the ceiling counts as one job, because the geyser is the root cause.
  3. Logs the public part with the municipality and reads back a reference. If you didn't say where the fault is, it asks for the suburb. A repeat report is linked to the existing case.
  4. Prices the private repair in rand, VAT included. Suspended suppliers are left out, and the household's budget limit and blocked suppliers are applied.
  5. Books only after an explicit yes. The hold is tied to that exact quote, works once and expires after 15 minutes. The language model can't confirm a booking. Only code can, after a clear yes. If a quote is over budget, Alexa says so before asking.

Fixwise also remembers the household across conversations. "Any news on the geyser?" and "Where's our stopcock?" still work in a new conversation days later. Beyond the booking it can:

  • explain a price ("Why is the first one more expensive?");
  • track the job from reported to fixed against a municipal SLA clock;
  • offer an escalation when the case is overdue, which it records but does not send;
  • suggest a realistic visit window (Mon–Sat, 07:00–17:00);
  • give you a signed .ics file to add to your own calendar, plus a parts list as amazon.co.za search links (nothing is ordered);
  • ask for a 1–5 star rating after the visit, and never suggest a badly rated supplier again;
  • create a signed, read-only, 7-day landlord page that the tenant shares themselves;
  • read a photo of the fault with Amazon Nova Lite.

In the console it also takes requests in Afrikaans and isiZulu (beta), which Nova Lite translates. Amazon Polly has no voice for either language, so replies use the device's own voice or captions.

On Echo Show-style screens, the tools return MCP Apps cards: an issue timeline, a quote carousel and a booking confirmation. Every action is written to a hash-chained audit trail that you can check by voice ("What have you done for me?").

What's simulated (labelled in the app and the video): the suppliers and prices are synthetic demo data, the municipal status feed is simulated, and bookings are demo bookings. The CivicOps municipal catalogue and its rules are real. The load-shedding note is a generic tip, not live schedule data. No card details are ever taken.

How we built it

  • MCP server. We wrote it with zero runtime dependencies (src/mcp/). It implements MCP 2025-11-25 over Streamable HTTP and validates the protocol-version and Origin headers. It has 11 annotated tools whose descriptions are written for Alexa+ reasoning, plus 3 ui:// MCP Apps resources.
  • OAuth 2.1 account linking, built to Alexa+'s rules (src/oauth/):
    • PKCE S256, with the RFC 8707 resource parameter bound at both steps.
    • Discovery documents per RFC 9728 and RFC 8414.
    • Pre-registered clients only, with no DCR and no OIDC.
    • Refresh tokens rotate, and reuse is detected.
    • Tokens are stored only as hashes.
    • Two mounts share one OAuth server: /mcp follows the MCP spec (401 with WWW-Authenticate), and /alexa/mcp follows the Alexa+ rule (401 with no header).
  • Simulated Alexa+ console (/console). Each turn works like this:
    1. Amazon Nova Lite (Bedrock Converse with tool use) reads the utterance and the server's live tools/list, then plans the tool calls.
    2. An MCP client calls the public /mcp endpoint over HTTP with a short-lived token for the demo household, the same way a real host would.
    3. The console shows the speech, the MCP Apps card and an on-screen trace of every real tools/call.

Code-level guards decide every yes or no, drop any booking, cancel or close the user didn't ask for, and fall back to a deterministic router if Bedrock is slow.

  • AWS (all of it in CDK, in Python):
    • Lambda (Node 22, arm64) behind a Function URL.
    • DynamoDB for household state.
    • SQS feeding a worker Lambda that asks Nova Lite for a background advisor note, which keeps the voice path fast.
    • EventBridge keep-warm pings.
    • SSM SecureString for secrets (hashes only).
    • CloudWatch alarms and an AWS Budget.

A Cloudflare Worker serves alexa.culltron.app, and the Lambda accepts only requests that carry the Worker's secret header. A Strands agent for AgentCore Runtime is in the repo but is not deployed. Production calls Bedrock directly.

  • Reused code. The CivicOps (MIT) municipal catalogue, classifier and audit chain, ported to JavaScript (see PROVENANCE.md). Everything else was written for Fixwise during the hackathon.

Challenges we ran into

  • Alexa+ OAuth isn't generic MCP OAuth. Alexa+ wants a 401 with no WWW-Authenticate header, no DCR and no OIDC. We solved it with two mounts on one OAuth server, and a token works on both.
  • Letting a model drive tools without letting it spend money. The model never sees the confirmation fields, and only code can redeem a held quote.
  • A lost-update race. The background advisor could overwrite a booking made while it was running. Our own judge client caught it. We fixed it by giving advice its own key, and added a regression test.
  • Partner-only tooling. The Alexa+ CLI, MCP Toolkit and Web Simulator are gated, so we built a simulated host and an open judge client. Details are in the friction log.

Accomplishments that we're proud of

  • A real model-driven agent (Nova Lite tool use) calling a real MCP server over HTTP, with every call visible in an on-screen trace.
  • OAuth 2.1 account linking that is Alexa+-compliant and proven live, plus a one-command judge client and an MCP Inspector transcript, both run against production.
  • Bookings that are safe by construction: a single-use hold, bound to one quote, that only code can confirm.
  • Speed and cost, measured live on 22 Oct 2026:
    • Tool-call round trip from Johannesburg via Cloudflare: p50 150–181 ms, max 275 ms. The Alexa+ limit is 500 ms.
    • Lambda server-side time per call: p50 10 ms.
    • Model cost: about $0.001 per 8-turn conversation.
  • Quality:
    • Lighthouse accessibility scores 100 on desktop and at 412 px and 375 px.
    • All 92 automated tests pass, and gitleaks is clean.
    • A browser smoke test runs on phone and desktop viewports after every deploy and rolls back automatically if it fails.

What we learned

Put guardrails in code, not in prompts. Nova Lite plans well, but it sometimes splits one report into two calls or drops half of a two-part request. Small deterministic passes after planning fixed this without needing a bigger model. Alexa+'s 500 ms budget also pushed us to keep every MCP tool deterministic and to use the model only where its answer can arrive a moment later.

What's next for Fixwise

  • A CivicOps municipal pilot to replace the simulated feed with real work-order status.
  • Onboarding verified, insured local trades.
  • Updates by SMS or email when the municipal case moves.
  • Insurance claim packs built from the audit chain.
  • Native Afrikaans and isiZulu voices once a TTS voice exists.
  • Licensed live load-shedding data.
  • On-device Alexa+ testing once the developer tools open up.

Built With

Share this project:

Updates

Submission history