Inspiration

Account takeovers don't start with a password. They start at the carrier: a SIM is moved to a new device, or calls are quietly forwarded, and from that moment every verification code goes to the attacker. The victim notices when their phone loses signal, and the standard advice, "call your carrier", means calling from a phone that is no longer theirs.

Carriers already expose exactly the signals that catch this early. The CAMARA open APIs (SIM Swap, Call Forwarding Signal, Device Reachability) answer one small question each. What's missing is a way for the person at home, or the son watching over his mother across town, to ask that question in plain words and get a plain answer. Alexa+ with MCP gave us that surface. Ask the Tower is the server behind it: one question, "is my line OK?", answered from the network itself, with consent built in and no phone number ever spoken or stored.

More details here : https://github.com/asishbose/askTheTower/blob/main/docs/idea-spark.md

What it does

Ask the Tower is an MCP server with three tools that Alexa+ (or any MCP client) can call:

  • line_is_ok: asks the carrier whether a line's SIM was swapped recently, whether its calls are being forwarded, and whether it's reachable, then answers with deterministic reason codes (OK, SIM_SWAPPED_RECENT, CALL_FORWARDING_SET, NO_CONSENT, CARRIER_ERROR, …) and a fixed sentence for each.
  • watch_line: turns on proactive alerts for a line. When the carrier reports a swap or forwarding, the watcher gets a text within seconds; an escalation chain can text further contacts after a delay.
  • Consent and binding: a line is bound once on the holder's own phone (the carrier's Number Verification API proves possession), and the holder grants a named person a watch on it, revocable any time. Every proactive text re-checks the grant first; a revoked grant means the alert is dropped and audited as SUPPRESSED_REVOKED.

The demo tells one story in three moments. Asish asks about his line: all clear. His SIM is moved; Tower catches it and, instead of sending him to his carrier from a phone the attacker holds, offers to place the call. Forwarding is set; both facts come back in one answer. Then Mom's line: Asish watches it, her SIM is swapped and his phone buzzes with a real SMS; she revokes the grant, the next swap sends nothing, and the audit chain records why.

Behind every answer the safety sentence is written by the policy, not the model. The model chooses the tool; the words that tell someone not to call from their phone are the same every time.

How we built it

On AWS, end to end.

  • Tower (the MCP server) runs on Amazon Bedrock AgentCore Runtime behind a JWT authorizer (Amazon Cognito). A reference agent on Amazon Nova, also on AgentCore Runtime, plays the Alexa+ part in the web chat and in the control room.
  • Carrier access goes through AgentCore Gateway: five CAMARA OpenAPI specs registered as targets, SigV4 inbound, OAuth2 client credentials outbound. A mock carrier on ECS Fargate implements the same APIs with a controllable clock and event injection; a real operator replaces it with no change to Tower.
  • Policy is a separate package: a pure function from carrier facts and consent to reason codes and phrasing, with the thresholds in one file and a generated decision table.
  • Consent, binding and alerts are Lambda functions behind API Gateway, with DynamoDB tables for users, lines, grants, watches and alert state, KMS for the HMAC that turns phone numbers into line ids, SNS for SMS, and EventBridge Scheduler for the polls and escalation ticks.
  • Audit is a per-line hash chain in DynamoDB that the line holder can read and that detects tampering.
  • Everything is Terraform, 160-odd resources, with a browser-based ops stepper that runs the whole pipeline (build, push, plan, deploy, seed, demo, e2e, latency, report) from one tab and keeps the evidence.

We built it as six Python services in one repo with a shared policy package, 1,000-plus unit tests, an e2e suite that runs the same three moments locally in compose and against the live AWS stack, and a latency harness that measures Tower's answer time through AgentCore.

Challenges we ran into

  • Per-call cold starts. Our first AgentCore runs answered in 7–8 seconds. Each MCP call without a session header landed on a fresh microVM. One Mcp-Session-Id per process took it to ~400 ms, and we learned that AgentCore recycles a session after a couple of hundred calls, so clients reconnect on a fresh id rather than fail.
  • Three-legged OAuth and the Gateway. Number Verification needs the phone in the loop (3LO), and AgentCore Gateway with an IAM authorizer doesn't support it. We kept the binding page talking to that one API directly and routed the other four through the Gateway, which turned out to be the honest architecture anyway: possession is proved once, at binding.
  • The first call after a cold start. Tower's warm-up opened a Gateway session that the first real call couldn't use. We moved the first call into the warm-up itself.
  • Two laptops, one account. Terraform state is local (the account's policies block S3 backends), and two of us deploying into the same account produced a stack neither state fully knew. We now run one operator per account and made the ops stepper refuse to reuse an image tag when the tree has uncommitted changes.
  • Corporate networks. Building images and talking to AWS behind a TLS-inspecting proxy needed an optional, conditional CA bundle in the Docker builds, and the demo's admin path, locked to one /32, needed re-opening every time the proxy's exit address moved.
  • Latency honesty. Our target was a p95 under 400 ms per call. From Ottawa, through the authorizer, store and audit writes, we measure ~500 ms. We report it rather than widen the gate.

Accomplishments that we're proud of

  • A safety answer that never changes: the reason codes decide the sentence, and the model can't paraphrase "don't call from your phone".
  • Consent that actually bites: a revoked grant stops a text that is already in flight, and the suppression is in the audit chain with its reason.
  • No phone number anywhere it could leak: HMAC line ids in every log, table and tool result; the number lives only where the carrier needs it.
  • Real texts from AWS to a real phone in the demo, triggered by carrier events, not by a script.
  • One command from a clean checkout to a running stack, with evidence (test reports, latency numbers, transcripts) produced by the same pipeline that deploys.

What we learned

  • MCP is a good fit for a question with a tiny surface and a serious answer. Three tools were enough; the work was in the policy and the consent model, not the protocol.
  • AgentCore's session semantics matter more than its API: session headers, warm-up and reconnects decided whether Tower felt instant or broken.
  • CAMARA's value is that the questions are standard. A carrier swap behind the Gateway is a configuration change.
  • Deterministic phrasing and an audit chain are what make a voice assistant trustworthy in a security moment, more than any model capability.
  • Build the ops tooling early. The stepper, the evidence files and the runbooks paid for themselves many times over in the last week.

What's next for AskTheTower

  • Ship the Alexa+ skill with the Echo placing the carrier-support call from next_step: call_carrier.
  • A second carrier behind the same Gateway, to show the swap with no Tower change, and the Number Verification flow through the Gateway once 3LO is supported there.
  • Reachability-based care alerts ("Mom's phone has been off the network since this morning") with the escalation chain to a neighbour, already built and tested locally.
  • Remote Terraform state and a per-environment account layout, so a team can deploy together.
  • Shave the fixed cost per call: the authorizer and the audit write are the next two things to measure.

Built With

  • alexa
  • amazon-api-gateway
  • amazon-bedrock
  • amazon-bedrock-agentcore
  • amazon-cloudfront
  • amazon-cognito
  • amazon-dynamodb
  • amazon-ecr
  • amazon-ecs
  • amazon-eventbridge
  • amazon-nova
  • amazon-sns
  • aws-fargate
  • aws-kms
  • aws-lambda
  • camara
  • docker
  • fastapi
  • fastmcp
  • htmx
  • mcp
  • oauth2
  • openapi
  • python
  • terraform
Share this project:

Updates

Submission history