Inspiration

Compliance analysts at mobile-money operators drown in alerts. Every morning starts with hundreds of flags from monitoring rules, each of which measures one narrow thing — fan-in, fan-out, structuring band, velocity — and says so. The analyst has to pull the transaction graph, read the KYC documents, name the typology, write up the finding, and decide whether to escalate. Most alerts are false positives; the real ones look exactly the same at the top of the queue. The backlog grows faster than the team can work it.

The question we wanted to answer: can an AI agent do the honest, boring first pass — pull the graph, read the documents, draft the case file, hand it to a human — without pretending to make the decision itself? The collaborative-partner framing in the hackathon tracks mapped onto this precisely: the agent's value is in the back-and-forth with a human analyst, not in operating unsupervised.

We also wanted the voice interface to be real, not a stretch. Compliance analysts don't sit at a desk all day; they walk between meetings, take calls, review cases while doing something else. An Echo Show in the corner of the office with Alexa+ as the orchestrator is a better fit for how the work actually happens than a dashboard.

What it does

OrionAML is a voice-driven anti-money-laundering investigator for Alexa+. On a live Kenyan M-PESA transaction graph:

  1. A suspicious pattern forms. Seven live typologies ship: SIM-farm cash-out, paybill turnover mismatch, structuring (smurfing), betting passthrough, rapid cash-out, mule layering, agent float cycle. Each writes real transactions into Neo4j as the pattern forms and streams graph updates to the Echo Show in real time via the MCP Apps extension.
  2. Alexa narrates it. Short spoken replies, no markdown, long identifiers read digit by digit via Amazon Polly SSML (e.g. paybill 845122 is "eight four five one two two", not "eight hundred forty-five thousand"). The light bar at the bottom of the Echo Show breathes when idle, runs when thinking, pulses when speaking.
  3. The agent investigates. PLAN → INVESTIGATE → VERIFY → REASON → ACT. The model picks tools from a FastMCP server (graph_query, agent_spread_check, structuring_check, extract_from_documents, screen_names, …) and chooses the trajectory itself — no routing code.
  4. It files a Linear issue. The analyst reviews the draft in Linear, questions it in-thread, and the webhook triggers the agent to re-verify against the graph and reply in the same thread. The agent never closes or escalates a case itself — that's the compliance-correct framing of "human in the loop."
  5. Everything is explainable. The case page carries the facts, the claims grouped by verified/rejected status, the agent's reasoning trace with the model's own stated reason for picking each tool, the falsifiers section ("what would change this conclusion"), and a draft SAR for the analyst to review.

How we built it

Alexa+ surface. Self-hosted FastMCP server over Streamable HTTP at /mcp, protocol version 2026-01-26. OAuth 2.1 client_credentials with Protected Resource Metadata (RFC 9728) and Authorization Server Metadata (RFC 8414) at .well-known/*. Bare 401 with no WWW-Authenticate header on unauthenticated /mcp requests — Alexa+ specifically requires this. A web simulator at /simulator calls the same FastMCP instance in-process so the simulated and real paths share tool schemas, results, and the working / check_progress holding protocol.

Model. Claude via the Anthropic SDK, with prompt caching (cache_control: {"type": "ephemeral"}) on the system block so tools + system ride the same cache prefix across every turn. Cache-hit telemetry logged on every call so a silent invalidator (a datetime.now() in the prompt, a non-deterministic tool order) would be immediately visible.

Voice. Amazon Polly, SSML-wrapped so long digit runs are read one digit at a time. Browser Web Speech API is the fallback when Polly returns 5xx.

Infrastructure. AWS Fargate behind CloudFront, Secrets Manager for keys, CodeBuild for CI deploys. Terraform-managed under infra/. Neo4j AuraDB Free for the transaction graph. Postgres (RDS) for Django models.

Linear integration. Raw httpx against Linear's GraphQL API. HMAC-signed webhook ingestion. Background threads for the agent's re-verify round-trip (Linear's 5s webhook timeout is way shorter than our 15-30s agent loop). Dedicated system prompt for the Linear text medium — markdown OK, numerals as numerals.

Challenges we ran into

  • Alexa+'s unusual auth requirement. The Alexa+ MCP toolkit requires a 401 with no WWW-Authenticate header. RFC 6750 says to include one. We only found this in the Alexa+ auth guide and ship a WWW-Authenticate-free 401 path explicitly for it.
  • MCP Apps 2026-01-26 spec discovery. The spec lives at github.com/modelcontextprotocol/ext-apps/blob/main/specification/2026-01-26/apps.mdx but the hackathon resources page links to a quickstart that doesn't name the version. We had to WebFetch the GitHub spec directly to extract the correct PROTOCOL_VERSION string.
  • Bedrock Marketplace EULA + Mantle routing. We tried to pivot the LLM path to Amazon Bedrock for the AWS Builder story. Standard Bedrock Converse worked, but anthropic.AnthropicBedrock (InvokeModel path) returned INVALID_PAYMENT_INSTRUMENT: A valid payment instrument must be provided. Your AWS Marketplace subscription... even with model access granted. Separately, AnthropicBedrockMantle returned 404 for every model ID we tried. We reverted to the Anthropic API for the submission.
  • Polly SSML escape bugs. A single stray & in the agent's prose breaks the SSML parse with a cryptic 400. We escape-first-then-substitute (html.escape(text, quote=False) → wrap in <speak> → regex-substitute long digit runs with <say-as interpret-as="digits">).
  • FastMCP's AppConfig is undocumented. AppConfig(csp, visibility, prefers_border, permissions, domain) fields are in the Pydantic model but not surfaced in FastMCP docs. We introspected AppConfig.model_fields at runtime to discover the correct field names.
  • Thinking blocks must echo back unchanged. Current Claude models reject conversation history where earlier turns were edited. The agent loop preserves response.content verbatim on every turn.
  • Linear voice bleed. We initially used the Alexa system prompt for the Linear reply path — the agent wrote comments like "paybill one one seven one seven zero collected..." (phoneticized numerals in text). A dedicated Linear system prompt fixed this.
  • Scenario timing for the demo. Finding the right pace between the fan-out, the sweep moment, the final narration, and the hold where judges can read the final graph took iteration — a few hundred milliseconds either way changes whether the sweep lands as a payoff or just appears.

Accomplishments we're proud of

  • No routing code. The agent's instruction describes PLAN → INVESTIGATE → VERIFY → REASON → ACT as intents, not state-machine nodes. A SIM-farm alert and a paybill-turnover alert produce different tool trajectories because the model chooses — not because an if-statement routes.
  • The sweep moment. The pre-sweep fan-out is deliberately capped at a payer count that keeps the on-screen network under the drawable ceiling, so when the sweep transaction fires it adds a new operator node + outbound edge rather than swapping one of the drawn payers out. The payoff beat is visually unmistakable.
  • Spec-correct MCP Apps. Write tools carry visibility=["model"] so a compromised view iframe can't invoke them. View resources declare csp={} and prefersBorder=true. Views adopt the spec's standardized CSS variables with fallbacks, so Alexa+'s theme can flow through on the Echo Show.
  • An Echo Show that actually feels like a device. The simulator preview carries a thick bezel with inner highlight, a camera lens dot, an "echo show" wordmark on the bottom bezel, a status bar with "alexa+" in cyan and a live clock, and an idle-breathing Alexa light bar that transitions to running on thinking and pulsing on speaking.
  • Honest observability. Cache-hit telemetry on every Claude call. Every agent step records the model's own stated reason for picking the tool it picked.

What we learned

  • MCP spec version discovery is harder than it should be. Current spec version strings should be prominent on vendor landing pages, not buried in GitHub subdirectories.
  • Bedrock has three distinct access concepts: model access (bedrock:ModelAccess), Marketplace subscription EULA, and the Mantle vs Converse endpoint distinction. All three must be granted for the Anthropic SDK's Bedrock path to work. The relationship isn't discoverable from a single doc page.
  • Voice-first UX changes everything about tool design. Tools whose results are long or structured need to be paired with an on-screen view (MCP Apps) because reading them aloud is user-hostile. The holding protocol for slow tools is as important as the tools themselves.
  • Prompt caching for an agent loop needs active telemetry. Without logging cache_read_input_tokens, a silent invalidator (a datetime, a UUID, a non-deterministic serialization) can zero out your savings and nothing visibly changes.
  • The Alexa system prompt for voice is actively wrong for a text medium. Reusing it across channels (voice + Linear comments) looked clean in code but produced phoneticized numerals in text that read as broken software.

What's next

  • Managed Agents + self-hosted sandbox for the analyst-facing surface. Case review workflows benefit from a persistent workspace with mounted case files, memory across sessions, and the docx/xlsx skills for SAR drafting.
  • Amazon Bedrock pivot once the Marketplace + Mantle access path is cleared.
  • Multi-tenant support so an operator can host OrionAML for multiple compliance teams with isolated graphs and Linear workspaces.
  • A second detector pass on each case — a cheaper model (Haiku) running a sanity check against the primary model's conclusion before the case goes to a human.

Built With

  • alexa-plus
  • amazon-polly
  • anthropic
  • asgi
  • aws-cloudfront
  • aws-codebuild
  • aws-fargate
  • aws-secrets-manager
  • boto3
  • claude
  • django
  • docker
  • fastmcp
  • graphql
  • linear
  • model-context-protocol
  • neo4j
  • oauth2
  • postgresql
  • pydantic
  • python
  • streamable-http
  • terraform
  • uvicorn
Share this project:

Updates

Submission history