Skip to main content
Glama

GlassCloud

Rokid Bridge 및 GlassBridge용 클라우드 서버 — 기기 연결, MCP 도구 릴레이, 갤러리가 포함된 이미지 저장소, 서버 측 LLM 장면 설명을 처리합니다.

기능

GlassCloud는 Rokid 스마트 글래스 앱을 위한 백엔드입니다. 다음 기능을 제공합니다:

  1. 기기 연결 — 웹 콘솔과 Android 앱 간의 QR 코드 또는 토큰 기반 페어링

  2. MCP 도구 릴레이 — WebSocket을 통해 글래스에서 Google API로 Gmail 및 Calendar 도구 호출을 프록시 처리

  3. 이미지 갤러리 — 글래스에서 업로드된 장면 이미지를 수신하고 메타데이터와 함께 저장하며 웹 갤러리에 표시

  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**에 접속하여 웹 콘솔을 엽니다.


구성

모든 환경 변수는 시작 시 Zod를 통해 검증됩니다. 필수 변수가 누락된 경우 서버는 누락된 필드를 출력하고 종료됩니다.

보안 키 생성

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

JWT_SECRET용으로 한 번, 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 유지 확인 핑 간격

WS_PONG_TIMEOUT_MS

10000

연결 종료 전 퐁 응답 대기 시간

.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 범위(Scopes) 참고: 이 앱은 gmail.readonly 및 calendar.events를 요청합니다. 100명 이상의 사용자가 있는 공개 배포의 경우, Google은 CASA 보안 평가를 요구합니다. 개인용 또는 테스트용인 경우 앱을 테스트 모드로 유지하십시오.


LLM 설정 (서버 측 장면 설명)

GlassCloud는 설명 없이 업로드된 이미지(예: Rokid 앱이 "Bypass LLM" 모드일 때)에 대해 자동으로 장면 설명을 생성할 수 있습니다. 생성된 설명은 다음과 같이 처리됩니다:

  • 데이터베이스에 저장되고 갤러리에 표시됨

  • 앱이 글래스에 즉시 표시할 수 있도록 업로드 API 응답으로 반환됨

구성

  1. /console에 로그인하고 헤더에서 **LLM Settings →**를 클릭합니다.

  2. 제공업체(Gemini 또는 ChatGPT)를 선택하고 API 키를 붙여넣습니다.

  3. 선택적으로 사용자 지정 프롬프트를 추가하고 하나를 활성 상태로 선택합니다.

기본 프롬프트는 다음과 같습니다: "이 이미지의 장면을 간결하게 설명하세요."

API 키

제공업체

획득 방법

Gemini

Google AI Studio → API keys

ChatGPT

OpenAI Platform → API keys

API 키는 ENCRYPTION_KEY(AES-256-GCM)를 사용하여 저장 시 암호화됩니다. API 키가 구성되지 않은 경우, 서버 측 LLM은 조용히 건너뛰며 설명이 비어 있는 이미지는 그대로 저장됩니다.


웹 콘솔 페이지

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

파일

예

JPEG, PNG 또는 WebP — 최대 20MB

scene_description

문자열

예

장면 설명 (빈 문자열 가능)

device_model

문자열

예

기기 모델명

device_instance_id

문자열

예

고유 기기 식별자

captured_at

문자열

아니오

이미지가 캡처된 ISO 8601 타임스탬프

image_width

숫자

아니오

이미지 너비 (픽셀)

image_height

숫자

아니오

이미지 높이 (픽셀)

llm_provider

문자열

아니오

기기에서 사용된 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

도구

설명

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 해시로만 저장되며, 1회용이고 5분 후 만료됨

  • 모든 /api/* 경로에 속도 제한 적용 (환경 변수를 통해 구성 가능)

  • 모든 응답에 Helmet 보안 헤더 적용

  • 공개 갤러리 토큰은 무작위 UUID이며, 재생성 시 이전 링크는 즉시 무효화됨


라이선스

MIT

Related MCP Connectors