ViewingGuardian + Lingo
Integrates with Amazon's Alexa+ and Fire TV ecosystem, providing an MCP server that gives Fire TV scene-level awareness for parental-control interventions and language transformation during playback; optionally uses Amazon Bedrock for AI scene analysis.
Integrates with ElevenLabs for voice-preserving dubbing of scenes and dialogue replacement, with fallback to demo URLs when no API key is configured.
Integrates with Home Assistant's Fire TV integration to enable real playback control on Fire TV devices.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ViewingGuardian + LingoPause the show and skip the scary part for my 6-year-old"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
# ViewingGuardian + Lingo
AI Guardian and Language Layer for Fire TV, built as a self-hosted MCP server for Alexa+.
Table of Contents
Related MCP server: InnerOS Ambient Guardian
What it is
ViewingGuardian + Lingo is an Alexa+ MCP server that gives Fire TV real-time, scene-level awareness of what is playing and who is watching. It does two things existing parental controls and language settings cannot:
Guardian interventions - Analyzes scenes against a child profile (age, sensitivities, household rules) and intervenes before harmful content hits: skipping jump scares, softening peril, replacing profanity, pausing intense moments.
Language transformation - Translates subtitles in real time, dubs scenes while preserving the original actor voice, and replaces specific dialogue with family-friendly alternatives - without leaving playback.
Every other Alexa+ Fire TV project answers questions. This one acts on live content.
Architecture
One self-hosted MCP server (spec 2025-11-25, Streamable HTTP) exposes two tool namespaces, with Alexa+ as the MCP client.
+-----------------------------------------------------------+
| ALEXA+ (MCP Client) |
| NLU, orchestration, UI rendering |
+-------------------------+---------------------------------+
| Streamable HTTP (POST /mcp)
| Authorization: Bearer TOKEN
v
+-----------------------------------------------------------+
| VIEWINGGUARDIAN + LINGO MCP SERVER |
| |
| +---------------------+ +--------------------------+ |
| | Guardian Tools | | Lingo Tools | |
| | - get_current_ctx | | - translate_subtitles | |
| | - analyze_scene | | - dub_scene | |
| | - skip_scene | | - replace_dialogue | |
| | - get_child_profile| | - get_lingo_status | |
| | - log_decision | | | |
| | - set_boundary | | | |
| +---------------------+ +--------------------------+ |
| |
| State Layer: scene metadata (JSON), child profiles, |
| intervention log |
+-------------------------+---------------------------------+
| Internal control channel
v
+-----------------------------------------------------------+
| FIRE TV RUNTIME (Renderer) |
| Playback control | Subtitle overlay | Audio stem switch |
| Guardian alert overlay |
+-----------------------------------------------------------+
Design decision: The MCP server is stateless per request but backed by a persistent state store. Alexa+ may call tools in any order, so the server reconstructs context from the session ID plus playback timestamp.
Code entry points
Reviewers can find the required integrations here:
Integration | File |
FastMCP server (spec 2025-11-25) | server/main.py |
Amazon Bedrock (scene analysis) | server/analysis.py |
ElevenLabs (voice-preserving dubbing) | server/lingo.py |
OAuth 2.1 + PKCE S256 | server/auth.py, server/well_known.py |
ASK-to-MCP shim (Developer Console) | server/ask_shim.py |
Guardian tools | server/tools/guardian.py |
Lingo tools | server/tools/lingo.py |
Scene metadata | server/data/whispering_woods_metadata.json |
Child profiles | server/data/profiles.json |
Prerequisites
Required:
Python 3.11 or higher - https://www.python.org/downloads/
uv (Python package manager) - https://docs.astral.sh/uv/
Cloudflare Tunnel (cloudflared) - https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/
An Amazon Developer account for the Alexa Developer Console
Optional (enhances the demo):
AWS account with Bedrock access (Claude 3 Sonnet enabled)
ElevenLabs API key for real voice-preserving dubbing
Home Assistant with the Fire TV integration for real playback control
The demo runs without AWS, ElevenLabs, or Home Assistant credentials. Scene metadata drives rule-based verdicts, and dubbing returns pre-rendered demo URLs. Real credentials unlock the live LLM and TTS paths.
Windows 11 - one-time prerequisites install
winget install Python.Python.3.12
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
winget install --id Cloudflare.cloudflared
Close and reopen PowerShell so PATH updates take effect. Verify:
python --version
uv --version
cloudflared --version
Setup
Step 1 - Clone or create the project
If you have the repo:
git clone REPO_URL viewingguardian-mcp
cd viewingguardian-mcp
If starting from scratch:
mkdir viewingguardian-mcp
cd viewingguardian-mcp
New-Item -ItemType Directory -Path "server","server\tools","server\data","tests","demo","demo\audio_stems","deploy" -Force
New-Item -ItemType File -Path "pyproject.toml",".env.example","README.md","LICENSE" -Force
New-Item -ItemType File -Path "server\__init__.py","server\main.py","server\auth.py","server\well_known.py","server\models.py","server\state.py","server\analysis.py","server\lingo.py","server\firetv.py","server\ask_shim.py" -Force
New-Item -ItemType File -Path "server\tools\__init__.py","server\tools\guardian.py","server\tools\lingo.py" -Force
New-Item -ItemType File -Path "server\data\profiles.json","server\data\whispering_woods_metadata.json" -Force
New-Item -ItemType File -Path "tests\__init__.py","tests\test_tools.py","tests\test_auth.py","tests\test_latency.py" -Force
New-Item -ItemType File -Path "deploy\Dockerfile" -Force
Step 2 - Install Python dependencies
From inside the project folder:
uv sync
This reads pyproject.toml, creates a .venv folder, and installs FastMCP, Uvicorn, boto3, httpx, and all other dependencies.
If you see "warning: No requires-python value found", verify that pyproject.toml contains:
requires-python = ">=3.11"
Step 3 - Create your .env file
Copy-Item .env.example .env
code .env
Fill in the required values (see Environment variables below). At minimum, set OAUTH_JWT_SECRET to a random string and OAUTH_CLIENT_ID to any identifier. AWS and ElevenLabs keys are optional for the demo.
Step 4 - Add the LICENSE file
Invoke-WebRequest -Uri "https://www.apache.org/licenses/LICENSE-2.0.txt" -OutFile "LICENSE"
Step 5 - Verify the setup
uv run python -c "import fastmcp, boto3, httpx; print('Dependencies OK')"
If this prints Dependencies OK, the environment is ready.
Environment variables
Variable | Required | Purpose |
OAUTH_ISSUER | Yes | Public HTTPS URL of your tunnel |
OAUTH_JWT_SECRET | Yes | Random string used to sign JWTs (64+ characters) |
OAUTH_CLIENT_ID | Yes | OAuth client identifier (e.g. viewingguardian-client) |
AWS_ACCESS_KEY_ID | No | Bedrock access - falls back to rule-based verdicts if absent |
AWS_SECRET_ACCESS_KEY | No | Bedrock access |
AWS_REGION | No | Defaults to us-east-1 |
BEDROCK_MODEL_ID | No | Defaults to anthropic.claude-3-sonnet-20240229-v1:0 |
ELEVENLABS_API_KEY | No | Voice-preserving dubbing - falls back to demo URLs |
HA_URL | No | Home Assistant URL for Fire TV bridge |
HA_TOKEN | No | Home Assistant long-lived access token |
SERVER_HOST | No | Defaults to 0.0.0.0 |
SERVER_PORT | No | Defaults to 8000 |
Generate a strong JWT secret with:
-join ((48..57) + (65..90) + (97..122) | Get-Random -Count 64 | ForEach-Object {[char]$_})
Copy the output into .env as OAUTH_JWT_SECRET.
Running the MCP server
Open a PowerShell window, navigate to the project folder, and start the server:
cd C:\Users\YOUR-USER\viewingguardian-mcp
uv run python -m server.main
You should see:
INFO: Started server process [xxxx]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
Leave this terminal running. The server must stay up for the entire session.
Verify the server is responding
In a new PowerShell window:
curl.exe http://localhost:8000/.well-known/oauth-authorization-server
Expected JSON includes:
"code_challenge_methods_supported": ["S256"]
If you see this, the server is live and the OAuth discovery layer works.
Exposing the server publicly
Alexa+ requires a public HTTPS endpoint. Use Cloudflare quick tunnel for development.
Step 1 - Start the tunnel
Open a second PowerShell window (leave the server terminal running):
cloudflared tunnel --url http://localhost:8000
Cloudflare will print output like:
Your quick Tunnel has been created! Visit it at:
https://random-words-here.trycloudflare.com
Copy this HTTPS URL. It is your public endpoint.
Step 2 - Update .env with the tunnel URL
Open .env and update:
OAUTH_ISSUER=https://random-words-here.trycloudflare.com
Save the file.
Step 3 - Restart the MCP server
The server reads .env at startup, so it must be restarted to pick up the new issuer.
In the server terminal, press Ctrl+C, then:
uv run python -m server.main
Step 4 - Verify the public URL
In a new PowerShell window:
curl.exe https://random-words-here.trycloudflare.com/.well-known/oauth-authorization-server
The issuer field in the output should now match your tunnel URL.
Important about Cloudflare quick tunnels
The URL changes every time cloudflared restarts. If you close the tunnel terminal, you will get a new URL and must repeat Steps 2-4.
Keep the tunnel terminal open through the entire session.
Prevent your laptop from sleeping while the tunnel is live (Settings > System > Power > Screen and sleep > set to Never).
Testing the server
Test 1 - MCP protocol with the Inspector
In a new PowerShell window:
npx @modelcontextprotocol/inspector
A browser will open at http://127.0.0.1:6274. Configure:
Field | Value |
Transport Type | Streamable HTTP |
URL | |
Authorization | (leave empty for local testing) |
Click Connect, then go to the Tools tab and click List Tools. You should see all 10 tools:
analyze_scene_for_child
dub_scene
get_child_profile
get_current_context
get_lingo_status
log_decision
replace_dialogue
set_boundary
skip_scene
translate_subtitles
Run the jump scare test. Select analyze_scene_for_child, enter:
{
"session_id": "demo-session-1",
"profile_id": "mia",
"timestamp_seconds": 80.0
}
Expected:
{
"verdict": "skip",
"reason": "Jump scare detected at 75s. Not suitable for Mia (age 7).",
"recommended_action": "skip_forward",
"skip_seconds": 12
}
Note: If the server has auth middleware enabled, the Inspector will receive 401 Unauthorized. For local testing, comment out mcp.add_middleware(AlexaAuthMiddleware()) in server/main.py, restart the server, and reconnect. Re-enable it before the Alexa demo.
Test 2 - ASK shim via curl
Create a test payload file:
@'
{"version":"1.0","request":{"type":"IntentRequest","intent":{"name":"AnalyzeSceneIntent","slots":{"child":{"name":"child","value":"Mia"}}}}}
'@ | Set-Content -Path test_intent.json -Encoding ascii
Send it to the local server:
curl.exe -X POST http://localhost:8000/alexa -H "Content-Type: application/json" --data "@test_intent.json"
Expected:
{
"version": "1.0",
"response": {
"outputSpeech": {
"type": "SSML",
"ssml": "<speak>This is a flagged scene. Jump scare detected at 60s. Not suitable for Mia (age 7). I recommend skipping forward 12 seconds. Say skip it to proceed.</speak>"
},
"shouldEndSession": false
}
}
Test the same against the tunnel:
curl.exe -X POST https://random-words-here.trycloudflare.com/alexa -H "Content-Type: application/json" --data "@test_intent.json"
Identical output confirms end-to-end routing through Cloudflare.
Test 3 - Unit tests
uv run pytest tests/ -v
Expected: all tests pass, including test_analyze_scene_jump_scare which verifies the guardian returns skip_forward at t=80.0.
Alexa Developer Console setup
The @alexa-ai/cli MCP Toolkit is currently behind a private preview. This repo uses the traditional Custom Skill path with an ASK-to-MCP shim at /alexa.
Step 1 - Create the skill
Log in to the Alexa Developer Console at https://developer.amazon.com/alexa/console/ask
Click Create Skill
Skill name: ViewingGuardian
Default language: English (US)
Model: Custom
Hosting: Provision your own
Click Create skill, then choose Start from Scratch
Step 2 - Configure the endpoint
In the left sidebar, click Build > Endpoint
Select HTTPS
Default Region URL: https://random-words-here.trycloudflare.com/alexa
SSL certificate: select "My development endpoint is a sub-domain of a domain that has a wildcard certificate from a certificate authority"
Click Save Endpoints
Step 3 - Configure Account Linking
In the left sidebar, click Tools > Account Linking
Toggle "Do you allow users to create an account or link to an existing account?" to ON
Configure:
Field | Value |
Authorization Grant Type | Authorization Code Grant |
Client ID | viewingguardian-client |
Client Secret | Your OAUTH_JWT_SECRET from .env |
Authorization URI | |
Access Token URI | |
Client Authentication Scheme | HTTP Basic (Recommended) |
Scope | playback:read,playback:control,profile:read,profile:write |
Critical:
Scopes must be comma-separated, no spaces. The console rejects any string with spaces (regex for word-characters-only).
In Advanced Settings, enable S256 as the PKCE code challenge method. Account linking fails without it.
Click Save.
Step 4 - Set the invocation name
In the left sidebar, click Build > Invocations
Set Skill Invocation Name: view guardian
Click Save Model
The default placeholder is "change name". If you do not replace it, the simulator will respond "I am not quite sure how to help you with that."
Step 5 - Build the interaction model
In the left sidebar, click Build > Interaction Model > Intents
Click + Add Intent
Name: AnalyzeSceneIntent
Add sample utterances, one per line:
what is playing
what is playing
is this okay for {child}
is this ok for {child}
check this scene
check this scene for {child}
is this suitable for {child}
is this okay for mia
In the left sidebar, click Slot Types > + Add Slot Type
Name: CHILD_NAME
Values:
Mia
mia
my daughter
my son
Go back to AnalyzeSceneIntent > Intent Slots > + Add Slot
Name: child, Type: CHILD_NAME
Click Save Model
Step 6 - Build the skill
Click Build skill at the top right. Wait for the green checkmark next to Interaction Model in the left sidebar.
Step 7 - Test in the simulator
Click the Test tab
Set the dropdown to Development
Type: open view guardian
Expected: "Viewing Guardian is ready. Ask me what is playing, or whether a scene is okay for your child."
Then type: ask view guardian is this okay for mia
Expected: "This is a flagged scene. Jump scare detected at 60s. Not suitable for Mia (age 7). I recommend skipping forward 12 seconds. Say skip it to proceed."
Take a screenshot - that is your end-to-end proof.
Running the demo
Complete demo sequence
Follow these steps in order. Each terminal must stay open.
Terminal 1 - MCP server:
cd C:\Users\YOUR-USER\viewingguardian-mcp
uv run python -m server.main
Terminal 2 - Cloudflare tunnel:
cloudflared tunnel --url http://localhost:8000
Copy the tunnel URL, update OAUTH_ISSUER in .env, then restart Terminal 1 so the server picks up the new issuer.
Terminal 3 - Alexa Developer Console:
In your browser:
Open the Developer Console, go to your skill, click Test tab
Set the dropdown to Development
Run these commands in sequence:
Command | What it demonstrates |
open view guardian | Skill launch, endpoint connectivity |
ask view guardian is this okay for mia | Full guardian flow with jump scare detection |
ask view guardian what is playing | Context retrieval |
ask view guardian check this scene for mia | Alternate phrasing of the same intent |
Terminal 4 - MCP Inspector (optional, for protocol proof):
npx @modelcontextprotocol/inspector
Connect to http://localhost:8000/mcp, list tools, and run analyze_scene_for_child with the jump scare timestamp.
Scene timeline for the mock film
The included demo film The Whispering Woods has these flagged moments:
Timestamp | Scene | Flag | Guardian action |
60-75s | Sudden silence before scare | pre_jump_scare | Skip forward 12s |
75-90s | Deer bursts from brush | jump_scare | Skip forward 12s |
125-140s | Mild profanity | mild_profanity | Replace dialogue |
150-170s | Ravine edge, log crossing | peril | Combined intervention |
170-195s | Peak peril moment | peak_moment | Skip forward 8s |
Demo video checklist
For the hackathon submission, your under-3-minute video should show:
Fire TV or Vega simulator running the mock film
Developer Console simulator responding to ask view guardian is this okay for mia
MCP Inspector showing all 10 tools and the analyze_scene_for_child verdict
Guardian intervention playing out on screen (skip, dub, or replacement)
The public tunnel URL visible in the browser address bar
Troubleshooting
Symptom | Cause | Fix |
NoCredentialsError on scene analysis | No AWS credentials configured | Expected - falls back to rule-based verdicts. Add credentials to .env to enable live Bedrock. |
ModuleNotFoundError: server.ask_shim | File not saved or wrong path | Test-Path server\ask_shim.py - if False, create it with New-Item -ItemType File -Path "server\ask_shim.py" -Force |
error: Failed to build on uv sync | pyproject.toml missing wheel target config | Add packages = ["server"] under [tool.hatch.build.targets.wheel] |
warning: No requires-python value found | Missing field in pyproject.toml | Add requires-python = ">=3.11" |
Cloudflare Error 1033 | Tunnel rotated or stopped | Restart cloudflared, update OAUTH_ISSUER in .env, restart the MCP server, update the Developer Console endpoint |
Alexa says I am not quite sure how to help you with that | Invocation name is the placeholder change name | Set it to view guardian and rebuild the model |
Alexa says There was a problem with the requested skill response | Endpoint returned invalid data | Check the server terminal for errors. Verify the endpoint URL ends in /alexa, not /mcp. |
OAuth scope rejected | Spaces in the Scope field | Use comma-separated scopes without spaces: playback:read,playback:control,profile:read,profile:write |
Account linking fails | S256 missing from metadata | Verify curl https://TUNNEL/.well-known/oauth-authorization-server includes code_challenge_methods_supported: [S256] |
401 Unauthorized from MCP Inspector | Auth middleware active | Comment out mcp.add_middleware(AlexaAuthMiddleware()) in server/main.py, restart, reconnect |
Tunnel URL changes every restart | Free Cloudflare quick tunnel limitation | Keep the tunnel terminal open. If it restarts, repeat the URL update steps. |
What we built during the hackathon
ViewingGuardian + Lingo was conceived and built entirely during the submission window. Nothing existed beforehand. This includes:
The MCP server with 10 tools (server/)
The ASK-to-MCP shim (server/ask_shim.py)
The OAuth 2.1 discovery implementation with PKCE S256
The mock film The Whispering Woods and its scene metadata
All Fire TV control and language transformation logic
The full test suite (unit, auth, latency)
The only pre-existing components are open source libraries (FastMCP, Uvicorn, boto3, httpx) and third-party APIs (Bedrock, ElevenLabs, Home Assistant).
Tracks and mini challenges
Track: Alexa+
Mini challenges: Open Source, AWS Builder
AWS services used
Amazon Bedrock (Claude 3 Sonnet) - scene analysis in server/analysis.py. Called via bedrock.invoke_model(). Falls back to rule-based verdicts when credentials are absent.
AWS Lambda / DynamoDB / ECS - planned production deployment targets (documented in deploy/).
Friction Log
Development friction encountered while building this project is documented in FRICTION_LOG.md.
License
Apache 2.0 - see LICENSE.
Related MCP Connectors
Transcribe, subtitle and dub videos into 100+ languages, and translate text.
Audio for your agent: transcribe, speak, translate, summarise, plus sound effects and music.
AI image, video, voice and music generation over MCP, routed to Veo 3.1, Seedance 2.5 and more.
Sentiment, toxicity, entity extraction, PII, translation, summary, QA, fraud scoring, safety audit.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables Alexa+ agents to maintain auditable operational continuity across shifts by turning speech into verifiable state, persisting unresolved work, refusing unverified actions, and requiring human approval before executing and confirming high-risk tasks.MIT
- AlicenseNot gradedqualityBmaintenanceEnables Alexa+ to collect recent security and IoT context, reason locally, prepare bounded physical actions for explicit approval, execute them through adapters, and return verified evidence in real time.MIT
- AlicenseNot gradedqualityBmaintenanceEnables household members to use Alexa+ in their own language and script, with shared lists, reminders, devices, and messages stored as spoken and rendered for each person.MIT
- AlicenseNot gradedqualityBmaintenanceEnables autonomous AI governance and pre-action verification for Alexa+ agents, blocking high-risk operations like unlocking doors or financial transactions while grounding decisions in verifiable policy and AWS Bedrock reasoning.MIT