Voice Assistant MCP Demo
by nhattan-dev
README.md
# Voice Assistant MCP Demo — Phase 1: Cloud Backend
Ba service Python chạy độc lập, kết nối qua URL env vars.
```
api_server → mcp_server → gateway ← Android app / curl
```
---
## Chạy local (test Phase 1)
```bash
pip install -r requirements.txt
cp .env.example .env # điền GEMINI_API_KEY và GATEWAY_SHARED_TOKEN
# Terminal 1
python api_server.py # :8000
# Terminal 2
python mcp_server.py # :8001 (MCP_BASE_URL=http://localhost:8000 đã có trong .env)
# Terminal 3
python gateway.py # :8002
```
Test bằng curl:
```bash
# Không có auth token (để trống GATEWAY_SHARED_TOKEN trong .env khi test local)
curl -X POST http://localhost:8002/command \
-H "Content-Type: application/json" \
-d '{"text": "việc hôm nay có gì", "session_id": "test-1"}'
# Kết quả mong đợi:
# {"speech": "Hôm nay bạn có 4 việc, trong đó 3 việc chưa xong...", "session_id": "test-1"}
# Multi-turn — follow-up cùng session
curl -X POST http://localhost:8002/command \
-H "Content-Type: application/json" \
-d '{"text": "đánh dấu xong việc gọi khách hàng", "session_id": "test-1"}'
```
---
## Deploy lên Render (3 service riêng)
### Bước 1 — Push repo lên GitHub
```bash
git init && git add . && git commit -m "init phase 1"
git remote add origin <your-github-repo-url>
git push -u origin main
```
### Bước 2 — Tạo Service 1: API Server
- **Render Dashboard** → New → Web Service → chọn repo
- Name: `task-api`
- Build Command: `pip install -r requirements.txt`
- Start Command: `python api_server.py`
- Environment Variables:
- `PORT` → để Render tự điền
Sau khi deploy xong, copy URL dạng `https://task-api-xxxx.onrender.com`.
### Bước 3 — Tạo Service 2: MCP Server
- Name: `task-mcp`
- Start Command: `python mcp_server.py`
- Environment Variables:
- `API_BASE_URL` → URL của Service 1 (bước 2)
Sau khi deploy, copy URL: `https://task-mcp-xxxx.onrender.com`.
### Bước 4 — Tạo Service 3: Gateway
- Name: `task-gateway`
- Start Command: `python gateway.py`
- Environment Variables:
- `MCP_BASE_URL` → URL của Service 2 (bước 3)
- `GEMINI_API_KEY` → key từ Google AI Studio
- `GATEWAY_SHARED_TOKEN` → chuỗi ngẫu nhiên (dùng `openssl rand -hex 16`)
### Bước 5 — Test end-to-end trên cloud
```bash
GATEWAY_URL=https://task-gateway-xxxx.onrender.com
TOKEN=your_shared_token
curl -X POST $GATEWAY_URL/command \
-H "Content-Type: application/json" \
-H "X-Auth-Token: $TOKEN" \
-d '{"text": "việc hôm nay có gì", "session_id": "s1"}'
```
> **Lưu ý cold-start:** Render free tier ngủ sau 15 phút idle. Request đầu tiên mất ~30s. Chấp nhận được cho demo.
---
## Biến môi trường tổng hợp
| Service | Biến | Bắt buộc | Ghi chú |
|---|---|---|---|
| api_server | `PORT` | auto | Render tự set |
| mcp_server | `PORT` | auto | |
| mcp_server | `API_BASE_URL` | ✓ | URL của api_server |
| gateway | `PORT` | auto | |
| gateway | `MCP_BASE_URL` | ✓ | URL của mcp_server |
| gateway | `GEMINI_API_KEY` | ✓ | Google AI Studio |
| gateway | `GATEWAY_SHARED_TOKEN` | khuyến nghị | Bảo vệ endpoint public |
---
## Cấu trúc request/response gateway
**POST `/command`**
Headers: `X-Auth-Token: <token>` (nếu có GATEWAY_SHARED_TOKEN)
Body:
```json
{
"text": "lệnh giọng nói đã chuyển thành text",
"session_id": "uuid-từ-app"
}
```
Response:
```json
{
"speech": "Câu trả lời tiếng Việt tự nhiên để đọc lên",
"session_id": "uuid-từ-app"
}
```
Session tự reset sau 30 phút không có request.
---
## Mốc Phase 1 ✓
```
curl POST /command {"text": "việc hôm nay có gì"}
→ {"speech": "Hôm nay bạn có ..."}
```
---
## Phase 2 — Android App (Push-to-Talk)
### Cấu trúc
```
android/
├── app/src/main/java/com/demo/voiceassistant/
│ ├── Config.kt ← URL gateway + auth token
│ ├── MainActivity.kt ← 1 nút "Nói", quản lý luồng
│ ├── SpeechController.kt ← STT vi-VN (SpeechRecognizer)
│ ├── ApiClient.kt ← POST /command qua OkHttp
│ └── SpeakController.kt ← TTS đọc kết quả
└── app/src/main/res/
└── layout/activity_main.xml
```
### Mở trong Android Studio
1. **File → Open** → chọn thư mục `android/`
2. Android Studio tự tải Gradle và sync dependencies
3. Cắm máy thật (hoặc bật AVD) → **Run**
> Nếu dùng máy ảo (AVD): chọn image có **Google Play** để `SpeechRecognizer` hoạt động với vi-VN.
### Luồng hoạt động Phase 2
```
Bấm nút "Nói"
→ SpeechRecognizer (vi-VN) nghe ~5 giây
→ text → POST https://task-gateway-0le7.onrender.com/command
header: X-Auth-Token: <token>
→ {"speech": "..."} → TextToSpeech đọc lên
```
**Lưu ý cold-start:** Request đầu tiên sau 15 phút idle mất ~30s (Render free tier). Timeout của ApiClient đã set 90s.
### Đổi gateway / token
Sửa [android/app/src/main/java/com/demo/voiceassistant/Config.kt](android/app/src/main/java/com/demo/voiceassistant/Config.kt):
```kotlin
object Config {
const val GATEWAY_URL = "https://task-gateway-xxxx.onrender.com"
const val AUTH_TOKEN = "your_token_here"
}
```
### Mốc Phase 2 ✓
Bấm nút → nói "việc hôm nay có gì" → app đọc lại danh sách bằng giọng tiếng Việt.
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues