Skip to main content
Glama
yomgui

World Clocks

by yomgui

World Clock Dashboard — example MCP App for OpenWork

A world clock dashboard with configurable cities, ticking local times, and a 12/24-hour display. The shared demo lets everyone edit one clock list. Per-member storage and token verification are implemented, but the authorization-server integration needed for settings to follow each member is not yet included.

Clocks are shown in time order; there is no manual ordering. On every client tick, clocks are sorted by their current UTC offset (earliest first, respecting DST), then by city name for ties. Your own zone has a you badge but is not pinned. If the visible limit is smaller than the saved set, the first N clocks in time order are shown; Edit shows the full set in the same order.

Preferences store a set of city ids plus hourCycle and limit, not a custom order. Existing stored preferences still load: legacy order/position fields and the old array ordering have no effect on display. Your cities and format are kept, but their visible order (and which fit within the limit) may change. New save_preferences calls reject order and position.

How it is built

A TypeScript MCP server exposes tools and one ui:// resource; React and Vite build a self-contained MCP App view. Browser Intl computes time and DST locally: the wire carries locations, not clock readings, and the view uses no browser storage. api/mcp.js loads the emitted server build for Vercel's shared-mode deployment.

From the repository root, using Node 24.20.0 and pnpm 11.4.0:

pnpm install --frozen-lockfile
pnpm run check                 # typecheck, build view, 118 tests
pnpm run build:server          # emitted Node build for api/mcp.js
AUTH_REQUIRED=false PORT=4321 pnpm run serve

The local endpoint is http://localhost:4321/mcp.

Related MCP server: Browser Tools for Claude Code

Persistence

Vercel Blob when BLOB_READ_WRITE_TOKEN is set; file when WORLD_CLOCKS_DATA_DIR is set; memory otherwise. Boot logs and preference/render tool results expose storeKind (blob, file, or memory) without credentials. Connect a private Blob store to the Vercel project to inject the token.

Blob uses one private JSON object at prefs/<key>.json, deterministic names, overwrite enabled, last-write-wins, and uncached origin reads (useCache: false). SDK 2.8 supports private access; no public-read fallback is used. Keys are sha256(iss|sub) in authenticated mode and shared when AUTH_REQUIRED=false. This changes the old anonymous shared key and org-suffixed authenticated keys; existing records are not migrated automatically. An issuer's same subject shares preferences across org claims. Shared mode intentionally shares edits among all callers.

Memory resets on process restart/cold start; file storage requires a persistent volume. Blob survives Vercel instances, but does not itself add an authorization-server integration.

Add it to an OpenWork dashboard

  1. Deploy the shared demo with the included vercel.json, or host the server at a public HTTPS origin with AUTH_REQUIRED=false.

  2. Open Settings › Library → custom MCP. Enter https://your-host.example/mcp and select Authentication None for shared mode.

  3. On the dashboard, choose Add app. In Den's picker, the World Clocks connection currently shows FOUR entries:

    • World Clocksshow_world_clocks, the dashboard tile.

    • Resolve locationsresolve_locations, an internal app-only helper.

    • Get my preferencesget_preferences, an internal app-only helper.

    • Save my preferencessave_preferences, an internal app-only write helper.

Add ONLY the first card, "World Clocks" (Run automatically). The other three are internal helpers the tile calls itself; do not add them as tiles — "Save my preferences" would write on every refresh.

Launch with default {} and choose your cities inside the tile; explicit cities launch arguments override saved preferences.

The three helpers retain their ui://world-clocks/mcp-app.html binding because the released host requires it for calls from the tile. Product finding: visibility: ["app"] with resourceUri currently leaks these helpers into Den's Add app picker. The picker should hide app-only helpers without removing the resource binding needed by the host; this is guidance, not a product fix.

For a future per-member deployment, supply an authorization server issuing JWTs specifically for this app. Set AUTH_REQUIRED=true, AUTH_ISSUER, AUTH_JWKS_URL, AUTH_AUDIENCE (this server's resource URL), PUBLIC_BASE_URL, and persistent WORLD_CLOCKS_DATA_DIR; connect with authType: oauth and credentialMode: per_member. Tokens for another resource, audience-less tokens, and opaque tokens are rejected; there is no userinfo fallback. The embedded authorization server and end-to-end personal sign-in flow are not implemented in this World Clocks repository. For an OpenWork-identity OIDC implementation, see yomgui/acme-home-demo (IDENTITY_MODE=openwork, server/oauth.ts, and server/upstream.ts), including its configuration and security caveats. That reference does not enable personal sign-in for World Clocks or establish hosted verification.

License

MIT — Copyright (c) 2026 OpenWork Labs.

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    A
    maintenance
    Enables controlling a real Chrome browser from MCP hosts like Claude, with extension-based or CDP fallback, supporting tabs, navigation, interaction, and page reading tools.
    40
    1,043 npm
    6
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables local-first management of tabs across multiple Chromium browsers through MCP, providing tab organization, search, personal context, and browser actions over a localhost-only interface.
    -