Skip to main content
Glama

GlassCloud

Rokid BridgeおよびGlassBridge用のクラウドサーバー — デバイスのリンク、MCPツールの中継、ギャラリー付きの画像ストレージ、サーバーサイドLLMによるシーン記述を処理します。

機能

GlassCloudはRokidスマートグラスアプリのバックエンドです。以下の機能を提供します:

  1. デバイスリンク — WebコンソールとAndroidアプリ間でのQRコードまたはトークンベースのペアリング

  2. MCPツール中継 — グラスからGoogle APIへのGmailおよびカレンダーのツール呼び出しをWebSocket経由でプロキシ

  3. 画像ギャラリー — グラスからアップロードされたシーン画像を受信し、メタデータと共に保存してWebギャラリーに表示

  4. サーバーサイドLLM — 説明のない画像が到着した際、自動的にGeminiまたはChatGPTを呼び出して説明を生成し、グラスに表示するためにアプリへ返信

  5. 公開ギャラリーリンク — ユーザーごとの公開共有トークンにより、ログインなしでギャラリーを閲覧可能

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  Rokid Glasses  │────▶│   GlassCloud    │────▶│  Google APIs    │
│  + Android App  │ WS  │  (This Server)  │     │  Gmail/Calendar │
└─────────────────┘     └─────────────────┘     └─────────────────┘
                               │  │
                    REST API   │  │  Server-side LLM
                               ▼  ▼
                        ┌─────────────────┐
                        │  Web Console    │
                        │  Gallery · LLM  │
                        │  Settings · QR  │
                        └─────────────────┘

クイックスタート

# 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 start

http://localhost:3000/console を開き、Webコンソールにアクセスします。


設定

すべての環境変数は起動時にZodで検証されます。必要な変数が不足している場合、サーバーは不足しているフィールドを表示して終了します。

シークレットの生成

node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"

これを2回実行してください。1回は JWT_SECRET 用、もう1回は ENCRYPTION_KEY 用です。

必須変数

変数

説明

JWT_SECRET

デバイスJWTの署名用。32文字以上。ランダムかつ秘密である必要があります。

ENCRYPTION_KEY

OAuthトークンおよびLLM APIキーの保存時の暗号化用(AES-256-GCM)。32文字以上。

GOOGLE_CLIENT_ID

Google OAuth 2.0 クライアントID

GOOGLE_CLIENT_SECRET

Google OAuth 2.0 クライアントシークレット

GOOGLE_CALLBACK_URL

OAuthリダイレクトURI — Google Cloud Consoleに登録されているものと一致する必要があります(例: https://yourserver.com/auth/google/callback)

オプション変数

変数

デフォルト

説明

PORT

3000

HTTPサーバーポート

NODE_ENV

development

development

production

test

LOG_LEVEL

info

trace

debug

info

warn

error

fatal

DATABASE_PATH

./data/glasscloud.db

SQLiteデータベースファイルのパス

CORS_ORIGINS

http://localhost:3000

許可されたCORSオリジンのカンマ区切りリスト

RATE_LIMIT_WINDOW_MS

60000

レート制限ウィンドウ(ミリ秒)

RATE_LIMIT_MAX_REQUESTS

60

IPごとのウィンドウあたりの最大リクエスト数(/api/* に適用)

WS_PING_INTERVAL_MS

30000

WebSocketキープアライブping間隔

WS_PONG_TIMEOUT_MS

10000

接続を閉じる前にpongを待機する時間

.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/callback

Google OAuthの設定

  1. Google Cloud Console → APIとサービス → 認証情報 に移動します

  2. OAuth 2.0 クライアントID(ウェブアプリケーション)を作成します

  3. 承認済みのリダイレクトURI にコールバックURLを追加します: https://yourserver.com/auth/google/callback

  4. プロジェクトで Gmail API および Google Calendar API を有効にします

  5. OAuth同意画面 → テストユーザー にテストユーザーを追加します(アプリがテストモードの間は必須です)

Googleスコープに関する注意: このアプリは gmail.readonly および calendar.events を要求します。100人以上のユーザーを抱える公開デプロイには、GoogleによるCASAセキュリティ評価が必要です。個人利用やテスト目的の場合は、アプリをテストモードのままにしてください。


LLM設定(サーバーサイドのシーン記述)

GlassCloudは、説明なしでアップロードされた画像(Rokidアプリが「LLMバイパス」モードの場合など)に対して、自動的にシーン記述を生成できます。生成された説明は以下の通りです:

  • データベースに保存され、ギャラリーに表示されます

  • アップロードAPIのレスポンスとして返され、アプリがグラス上に即座に表示できるようにします

設定

  1. /console にサインインし、ヘッダーの LLM Settings → をクリックします

  2. プロバイダー(Gemini または ChatGPT)を選択し、APIキーを貼り付けます

  3. 必要に応じてカスタムプロンプトを追加し、1つをアクティブとして選択します

デフォルトのプロンプトは: "Describe the scene in this image concisely." です。

APIキー

プロバイダー

取得先

Gemini

Google AI Studio → API keys

ChatGPT

OpenAI Platform → API keys

APIキーは ENCRYPTION_KEY (AES-256-GCM) を使用して保存時に暗号化されます。APIキーが設定されていない場合、サーバーサイドLLMは静かにスキップされ、説明が空の画像はそのまま保存されます。


Webコンソールページ

URL

説明

/console

ダッシュボード: 統計、デバイスリンク、MCPサービス管理

/console/link

デバイスペアリング用のQRコード/リンクトークンの生成

/console/gallery

画像ギャラリー(サムネイル、詳細、削除を含むテーブルビュー)

/console/gallery/photo/:id

メタデータ付きのフルサイズ画像

/console/settings/llm

LLMプロバイダー、APIキー、プロンプト管理

/public/gallery/:token

公開ギャラリー(ログイン不要)

/public/gallery/:token/photo/:id

公開写真の詳細


REST APIエンドポイント

デバイスと認証

エンドポイント

メソッド

認証

説明

/health

GET

なし

接続統計を含むヘルスチェック

/auth/google

GET

なし

Google OAuthフローの開始

/auth/google/callback

GET

なし

OAuthコールバック

/api/link/exchange

POST

なし

リンクトークンとデバイスJWTの交換

/api/devices

GET

デバイスJWT

リンクされたデバイスのリスト

/api/devices/:id

DELETE

セッション

デバイスのリンク解除

画像

エンドポイント

メソッド

認証

説明

/api/images/upload

POST

デバイスJWT

画像とメタデータのアップロード (multipart/form-data)

/api/images/:id

GET

デバイスJWT

IDによる画像メタデータの取得

アップロードフィールド

フィールド

型

必須

説明

image

file

はい

JPEG, PNG, または WebP — 最大20 MB

scene_description

string

はい

シーンの説明(空文字でも可)

device_model

string

はい

デバイスモデル名

device_instance_id

string

はい

一意のデバイス識別子

captured_at

string

いいえ

画像がキャプチャされたISO 8601タイムスタンプ

image_width

number

いいえ

画像の幅(ピクセル)

image_height

number

いいえ

画像の高さ(ピクセル)

llm_provider

string

いいえ

デバイス上で使用されたLLM (例: Gemini, ChatGPT, None)

scene_description が空で、サーバーサイドLLMが設定されている場合、サーバーはLLMを呼び出し、レスポンスに llmDescription を含めます:

{
  "imageId": "uuid",
  "originalFilename": "uuid.jpg",
  "thumbnailFilename": "uuid_thumb.jpg",
  "receivedAt": 1714000000000,
  "capturedAt": 1714000000000,
  "llmDescription": "A person walking through a crowded market..."
}

MCP

エンドポイント

メソッド

認証

説明

/api/mcp/services

GET

デバイスJWT

利用可能なMCPサービスとツールのリスト


WebSocketプロトコル

接続先: ws://localhost:3000/ws?deviceId=YOUR_DEVICE_ID

クライアント → サーバー

// 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": "..." }

サーバー → クライアント

// 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" }

利用可能なMCPツール

Gmail

ツール

説明

gmail.get_unread

未読メール数と概要の取得

gmail.search

クエリによるメール検索

gmail.get_message

IDによるメール本文の取得

カレンダー

ツール

説明

calendar.get_today

今日の予定を取得

calendar.get_events

N日分の予定を取得

calendar.create_event

新しい予定の作成


データベーススキーマ

データベースはWALモードのSQLiteです。スキーマは起動時に作成され、新しいカラムはべき等なマイグレーションによって追加されます。

-- 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, ...)

ディレクトリ構造

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 types

データファイルは ./data/ に書き込まれます:

  • ./data/glasscloud.db — SQLiteデータベース

  • ./data/images/ — アップロードされた元の画像

  • ./data/thumbnails/ — 400×400 JPEGサムネイル(Sharpによって生成)


セキュリティ

  • OAuthトークン は ENCRYPTION_KEY を使用してAES-256-GCMで保存時に暗号化されます

  • LLM APIキー は同じキーで保存時に暗号化されます

  • デバイスJWT は JWT_SECRET (HS256) で署名され、すべてのAPIリクエストで検証されます

  • リンクトークン はSHA-256ハッシュとしてのみ保存され、使い捨てで5分で期限切れになります

  • すべての /api/* ルートで レート制限 を実施(環境変数で設定可能)

  • すべてのレスポンスに Helmet セキュリティヘッダーを適用

  • 公開ギャラリートークン はランダムなUUIDであり、再生成すると以前のリンクは即座に無効化されます


ライセンス

MIT

Related MCP Connectors