index01-mcp-alexa
by awwaiid
README.md
# Pebble Index 01 → Alexa bridge
Say **"Tell Alexa to set an alarm for 5 minutes"** into the ring, and your Echo sets the alarm.
## How the pieces fit
```
Index 01 ring ──BLE──▶ Pebble app (transcribes) ──▶ Pebble cloud agent (LLM)
│ MCP tool call: tell_alexa("set an alarm for 5 minutes")
▼
cloudflared tunnel ──▶ this server (server.py, Streamable HTTP MCP)
│ Alexa "text command" via aioamazondevices
▼
your Echo (as if you had spoken it)
```
Why this shape:
- **Pebble side.** The Index 01 supports "bring your own MCP server", but only *cloud-hosted* servers over
Streamable HTTP or SSE, and only `Authorization`-header auth (no OAuth). Custom MCP tools run in the
double-click "MCP Sandbox" with the cloud agent; single click stays the offline note-taker.
- **Alexa side.** Amazon has no public "send a voice command" API. The workable route is the same
unofficial endpoint Home Assistant's *Alexa Devices* integration uses (`send_text_command`), via the
`aioamazondevices` library. Its own test script uses it to set a timer, so alarms/timers are the canonical case.
## Setup
### 1. Amazon prerequisite: app-based 2FA
The library logs in with email + password + a TOTP code. Your Amazon account must have an authenticator
app (Google Authenticator, Authy, 1Password...) as its 2FA method. SMS-only 2FA does not work.
### 2. Install and log in once
```bash
cp .env.example .env
# fill in AMAZON_EMAIL, AMAZON_PASSWORD, and MCP_BEARER_TOKEN (openssl rand -hex 32)
uv sync
uv run alexa_login.py # asks for your 6-digit authenticator code
```
It saves `data/login_data.json` and prints your Echo devices. Put the one you want (e.g. `Kitchen Echo`)
in `ALEXA_DEFAULT_DEVICE` in `.env`.
### 3. Run the server
```bash
uv run server.py # listens on 127.0.0.1:8765
```
Quick sanity check:
```bash
curl -s localhost:8765/healthz
curl -s -X POST localhost:8765/mcp \
-H "Authorization: Bearer $MCP_BEARER_TOKEN" \
-H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
```
### 4. Expose it publicly with cloudflared
For a quick test (URL changes each run):
```bash
cloudflared tunnel --url http://localhost:8765
```
For something permanent, make a named tunnel on a hostname you own, e.g. `alexa.example.com`:
```bash
cloudflared tunnel login
cloudflared tunnel create alexa-bridge
cloudflared tunnel route dns alexa-bridge alexa.example.com
cat > ~/.cloudflared/config.yml <<EOF
tunnel: alexa-bridge
credentials-file: $HOME/.cloudflared/<TUNNEL-UUID>.json
ingress:
- hostname: alexa.example.com
service: http://localhost:8765
- service: http_status:404
EOF
cloudflared tunnel run alexa-bridge
```
`systemd/` has user units to keep both the server and the tunnel running.
### 5. Add it to the Pebble app
In the Pebble app, **Index tab → MCP & Tool Settings**:
1. **Create a sandbox group.** Model: *Default* (bump to *High Capability* only if it picks tools unreliably).
2. **MCP Servers tab → add server:**
- Name: `alexa`
- URL: `https://<your-public-host>/mcp` (or `/alexa/mcp` when served under the `BASE_PATH` prefix; see DEPLOY.md for our setup)
- Type: **Streamable**
- Authorization: `Bearer <your MCP_BEARER_TOKEN>` — the word `Bearer` is required
- Group: the sandbox group from step 1
3. **Index settings → "Double click and hold"** → assign that sandbox group.
Now: double-click-and-hold the ring, say "Tell Alexa to set an alarm for 5 minutes", release.
The cloud agent calls `tell_alexa`, and you get a phone notification with the tool result.
Gotcha from the community: the app caches the tool list for ~30 s after a config change. If tools don't
show up, toggle the transport SSE → Streamable to force a refetch.
## Tools exposed
| Tool | What it does |
|------|--------------|
| `tell_alexa(command, device?)` | Strips "tell Alexa to", lowercases, sends as a text command to the named or default Echo |
| `list_alexa_devices()` | Lists Echo devices and online state (handy for picking `ALEXA_DEFAULT_DEVICE`) |
## Caveats
- **Unofficial Amazon API.** It is the same one Home Assistant's core Alexa Devices integration relies on,
so it is well exercised, but Amazon can change it. If calls start failing with auth errors, rerun `alexa_login.py`.
- **Cloud dependency.** Custom MCP tools only run through Pebble's cloud agent, so the transcript of the
double-click request leaves your phone. Single-click notes still stay local.
- **Alarms land on the Echo,** not on the ring or phone. That is the point here, but the "device" argument
lets you aim at a different Echo ("...on the bedroom echo").
- **Amazon Japan** uses a different login flow and is not supported by the library.
## Alternatives considered
- **Home Assistant as the gateway.** If you already run HA, its *Alexa Devices* integration plus the
*MCP Server* integration could replace this repo: expose an HA script that calls
`alexa_devices.send_text_command` to Assist, and point Pebble at `https://<ha>/api/mcp/assist` with a
long-lived access token. More moving parts if you don't already have HA.
- **Alexa Routine triggered by a virtual sensor.** Routines can run a "custom" command as an action, but the
text is fixed at routine-creation time, so "5 minutes" can't be dynamic. Dead end for this use case.
- **Alexa Voice Service.** Would create alarms on a virtual device, not your Echo.
- **Pebble webhook instead of MCP.** The Index can POST the raw transcription to a URL. That would work too,
but you'd have to parse "tell alexa to ..." yourself and it fires for *every* recording on that button.
MCP lets the agent decide when Alexa is meant.
## License
MIT, see [LICENSE](LICENSE).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues