GlassCloud
Click on "Install 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., "@GlassCloudSummarize my unread emails from this morning."
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.
GlassCloud
Cloud server for Rokid Bridge and GlassBridge — handles device linking, MCP tool relay, image storage with gallery, and server-side LLM scene description.
What It Does
GlassCloud is the backend for Rokid smart glasses apps. It provides:
Device Linking — QR code or token-based pairing between the web console and the Android app
MCP Tool Relay — Proxies Gmail and Calendar tool calls from the glasses to Google APIs via WebSocket
Image Gallery — Receives scene images uploaded from the glasses, stores them with metadata, and displays them in a web gallery
Server-Side LLM — When an image arrives without a description, automatically calls Gemini or ChatGPT to generate one, then returns it to the app for display on the glasses
Public Gallery Links — Per-user public share tokens so galleries can be viewed without logging in
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Rokid Glasses │────▶│ GlassCloud │────▶│ Google APIs │
│ + Android App │ WS │ (This Server) │ │ Gmail/Calendar │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
REST API │ │ Server-side LLM
▼ ▼
┌─────────────────┐
│ Web Console │
│ Gallery · LLM │
│ Settings · QR │
└─────────────────┘Quick Start
# Install dependencies
npm install
# Copy and fill in environment variables
cp .env.example .env
# Edit .env — see Configuration section below
# Development (auto-reload)
npm run dev
# Production build + run
npm run build
npm startOpen http://localhost:3000/console to access the web console.
Configuration
All environment variables are validated at startup with Zod. The server will print the missing fields and exit if required variables are absent.
Generating Secrets
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"Run this twice — once for JWT_SECRET, once for ENCRYPTION_KEY.
Required Variables
Variable | Description |
| Signs device JWTs. Min 32 chars. Must be random and secret. |
| Encrypts OAuth tokens and LLM API keys at rest (AES-256-GCM). Min 32 chars. |
| Google OAuth 2.0 client ID |
| Google OAuth 2.0 client secret |
| OAuth redirect URI — must match what's registered in Google Cloud Console (e.g. |
Optional Variables
Variable | Default | Description |
|
| HTTP server port |
|
|
|
|
|
|
|
| SQLite database file path |
|
| Comma-separated list of allowed CORS origins |
|
| Rate limit window in milliseconds |
|
| Max requests per window per IP (applies to |
|
| WebSocket keepalive ping interval |
|
| Time to wait for pong before closing connection |
Example .env
PORT=3000
NODE_ENV=production
LOG_LEVEL=info
DATABASE_PATH=./data/glasscloud.db
JWT_SECRET=<generate with command above>
ENCRYPTION_KEY=<generate with command above>
CORS_ORIGINS=https://yourserver.com
GOOGLE_CLIENT_ID=123456789.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-xxxxx
GOOGLE_CALLBACK_URL=https://yourserver.com/auth/google/callbackGoogle OAuth Setup
Create an OAuth 2.0 Client ID (Web application)
Add your callback URL to Authorized redirect URIs:
https://yourserver.com/auth/google/callbackEnable the Gmail API and Google Calendar API in the project
Add test users under OAuth consent screen → Test users (required while app is in Testing mode)
Note on Google Scopes: This app requests
gmail.readonlyandcalendar.events. For public deployment with more than 100 users, Google requires a CASA security assessment. For private or testing use, keep the app in Testing mode.
LLM Settings (Server-Side Scene Description)
GlassCloud can automatically generate scene descriptions for images uploaded without one (e.g. when the Rokid app is in "Bypass LLM" mode). The generated description is:
Saved to the database and shown in the gallery
Returned in the upload API response so the app can display it on the glasses immediately
Configuration
Sign in at
/consoleand click LLM Settings → in the headerSelect a provider (Gemini or ChatGPT) and paste your API key
Optionally add custom prompts and select one as active
The default prompt is: "Describe the scene in this image concisely."
API Keys
Provider | Where to get it |
Gemini | |
ChatGPT |
API keys are encrypted at rest using ENCRYPTION_KEY (AES-256-GCM). If no API key is configured, server-side LLM is silently skipped and images with empty descriptions are stored as-is.
Web Console Pages
URL | Description |
| Dashboard: stats, device linking, MCP service management |
| Generate QR code / link token for pairing a device |
| Image gallery (table view with thumbnails, details, delete) |
| Full-size image with metadata |
| LLM provider, API key, and prompt management |
| Public gallery (no login required) |
| Public photo details |
REST API Endpoints
Device & Auth
Endpoint | Method | Auth | Description |
| GET | None | Health check with connection stats |
| GET | None | Initiate Google OAuth flow |
| GET | None | OAuth callback |
| POST | None | Exchange link token for device JWT |
| GET | Device JWT | List linked devices |
| DELETE | Session | Unlink a device |
Images
Endpoint | Method | Auth | Description |
| POST | Device JWT | Upload image + metadata (multipart/form-data) |
| GET | Device JWT | Get image metadata by ID |
Upload fields
Field | Type | Required | Description |
| file | Yes | JPEG, PNG, or WebP — max 20 MB |
| string | Yes | Scene description (may be empty string) |
| string | Yes | Device model name |
| string | Yes | Unique device identifier |
| string | No | ISO 8601 timestamp when image was captured |
| number | No | Image width in pixels |
| number | No | Image height in pixels |
| string | No | LLM used on-device (e.g. |
If scene_description is empty and a server-side LLM is configured, the server will call the LLM and include llmDescription in the response:
{
"imageId": "uuid",
"originalFilename": "uuid.jpg",
"thumbnailFilename": "uuid_thumb.jpg",
"receivedAt": 1714000000000,
"capturedAt": 1714000000000,
"llmDescription": "A person walking through a crowded market..."
}MCP
Endpoint | Method | Auth | Description |
| GET | Device JWT | List available MCP services and tools |
WebSocket Protocol
Connect at: ws://localhost:3000/ws?deviceId=YOUR_DEVICE_ID
Client → Server
// Execute a tool
{ "type": "tool_execute", "requestId": "uuid", "serverId": "gmail",
"toolName": "gmail.get_unread", "arguments": { "maxResults": 10 } }
// Link device to user account
{ "type": "link_device", "requestId": "uuid",
"linkToken": "from-qr-code", "deviceId": "..." }
// Get user account info
{ "type": "get_user_account", "requestId": "uuid", "deviceId": "..." }Server → Client
// Immediate progress (for voice feedback while tool runs)
{ "type": "tool_progress", "requestId": "uuid",
"status": "executing", "message": "Checking your emails..." }
// Tool result
{ "type": "tool_result", "requestId": "uuid",
"result": { "success": true, "content": "You have 3 unread emails..." } }
// Error
{ "type": "error", "requestId": "uuid", "error": "Token expired" }Available MCP Tools
Gmail
Tool | Description |
| Get unread email count and summaries |
| Search emails by query |
| Get full email content by ID |
Calendar
Tool | Description |
| Get today's events |
| Get events for N days |
| Create a new event |
Database Schema
The database is SQLite with WAL mode. Schema is created on startup; new columns are added via idempotent migrations.
-- Users (from Google OAuth)
users (
id, google_id, email, display_name, profile_picture_url,
public_gallery_token, -- UUID for public gallery share link
llm_provider, -- 'Gemini' or 'ChatGPT' (default: 'Gemini')
openai_api_key_encrypted, -- AES-256-GCM encrypted
gemini_api_key_encrypted, -- AES-256-GCM encrypted
active_prompt_id, -- FK to llm_prompts.id
created_at, updated_at
)
-- Linked devices
devices (id, user_id, device_name, device_model, last_seen_at, last_heartbeat_at, linked_at, created_at)
-- QR code link tokens (single-use, 5 min expiry)
link_tokens (id, user_id, expires_at, used_at, used_by_device_id, created_at)
-- Encrypted Google OAuth tokens
oauth_tokens (id, user_id, provider, access_token_encrypted, refresh_token_encrypted, ...)
-- Scene images from glasses
glass_images (
id, device_model, device_instance_id,
scene_description, -- May be empty; server-side LLM fills it if blank
original_filename, thumbnail_filename,
captured_at, received_at, created_at,
image_width, image_height, -- Pixels (nullable)
user_id, -- FK to users.id (nullable for legacy rows)
llm_provider -- Which LLM generated the description
)
-- User-defined LLM prompts
llm_prompts (id, user_id, name, text, created_at)
-- Third-party MCP servers registered via console
third_party_mcp_services (id, name, description, owner_user_id, endpoint_url, auth_type, auth_config_encrypted, tools_json, ...)Directory Structure
src/
├── index.ts # Entry point — starts HTTP + WebSocket servers
├── config/
│ ├── env.ts # Zod environment validation (exits on invalid config)
│ └── mcp-services.ts # Built-in MCP service definitions
├── server/
│ ├── express.ts # Express app — middleware, route mounting, error handling
│ ├── sessions.ts # In-memory session store for console auth
│ └── websocket.ts # WebSocket server with zombie connection cleanup
├── routes/
│ ├── auth.ts # Google OAuth flow
│ ├── console.ts # Web console dashboard
│ ├── devices.ts # Device management API
│ ├── gallery.ts # Image gallery (authenticated + public)
│ ├── health.ts # Health check
│ ├── images.ts # Image upload + retrieval API
│ ├── link.ts # QR code token generation
│ ├── mcp.ts # MCP services API
│ └── settings.ts # LLM settings page
├── services/
│ ├── auth.service.ts # OAuth + token refresh mutex
│ ├── device.service.ts # Device CRUD
│ ├── image.service.ts # Image save/list/delete + thumbnail generation
│ ├── link.service.ts # QR code token handling
│ ├── llm.service.ts # LLM settings CRUD + callLlm() (Gemini / ChatGPT)
│ ├── mcp-proxy.service.ts # MCP tool execution with input coercion
│ └── mcp-registry.service.ts # Third-party MCP server registry
├── websocket/
│ ├── handler.ts # Message routing with progress feedback
│ ├── protocol.ts # Message type definitions
│ └── connection.ts # Connection tracking
├── db/
│ ├── index.ts # SQLite connection + WAL mode
│ └── schema.ts # CREATE TABLE + ALTER TABLE migrations
├── utils/
│ ├── crypto.ts # AES-256-GCM encrypt/decrypt + token generation
│ ├── logger.ts # Pino structured logging
│ └── cache.ts # LRU cache for MCP tool results
└── types/
├── api.ts # REST API types
├── mcp.ts # MCP types
└── websocket.ts # WebSocket message typesData files are written to ./data/:
./data/glasscloud.db— SQLite database./data/images/— Original uploaded images./data/thumbnails/— 400×400 JPEG thumbnails (generated by Sharp)
Security
OAuth tokens encrypted at rest with AES-256-GCM using
ENCRYPTION_KEYLLM API keys encrypted at rest with the same key
Device JWTs signed with
JWT_SECRET(HS256); verified on every API requestLink tokens stored as SHA-256 hash only; single-use; 5-minute expiry
Rate limiting on all
/api/*routes (configurable via env)Helmet security headers on all responses
Public gallery tokens are random UUIDs; regenerating one instantly revokes the previous link
License
MIT
This server cannot be installed
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/jerryliu816/glasscloud'
If you have feedback or need assistance with the MCP directory API, please join our Discord server