Skip to content

hosts_list / MCP tools return "host: offline" when working on a local host #6160

Open

Description

@jnelken

Summary

When expose_host_service_via_relay is off for a host (the default — confirmed in ~/.superset/local.db's settings table), that host never opens its relay tunnel, so hosts_list (and the mcp__superset__* MCP tools, and presumably the mobile/web app) show it as online: false indefinitely. This is arguably correct behavior — the setting is an intentional opt-in for remote reachability — but nothing in the API response, CLI, or MCP tool descriptions distinguishes "relay disabled by local setting" from "genuinely unreachable/crashed."

Why this is confusing

  • hosts_list returns a bare { online: false } with no reason field.
  • Restarting host-service (or the whole app) doesn't change this, since the gate is the local setting, not process health — so the natural troubleshooting step (restart it) looks like it silently fails.
  • The only way I found to confirm the actual cause was extracting the Electron app.asar and reading dist/main/index.js to find:
    const exposeViaRelay = row?.exposeHostServiceViaRelay ?? false;
    if (exposeViaRelay && effectiveRelayUrl) childEnv.RELAY_URL = effectiveRelayUrl;
    and then confirming expose_host_service_via_relay was NULL in the local sqlite settings table.
  • This matters in practice for anyone driving mcp__superset__workspaces_* / terminals_* from an agent running locally on the same machine as the host — those calls are host-scoped and silently fail/hang for a host with relay disabled, with no indication in the tool description or error that this is the cause.

Suggested improvements (any one of these would help)

  1. hosts_list (and the CLI's superset hosts list) include a relayEnabled/relayConfigured field distinct from online, so "off by choice" is distinguishable from "down."
  2. Surface the setting more discoverably — e.g. superset status or superset hosts mentioning relay is disabled and how to enable it, rather than requiring a settings-UI hunt or reverse engineering.
  3. MCP tool descriptions for host-scoped tools (workspaces_create, terminals_*, etc.) mention that they require the target host's relay to be enabled, and/or return a clear error (rather than failing/hanging) when it isn't.

Workaround

For anyone hitting this: the local superset CLI (~/.superset/bin/superset) talks directly to http://127.0.0.1:48214 and works regardless of relay status — useful for host-scoped operations on the machine an agent is already running on.

Environment

  • Superset Desktop 1.18.3, macOS (arm64)

Activity

  1. changed the title [-]hosts_list / MCP tools give no signal that a host's relay is disabled by default[/-] [+]hosts_list / MCP tools return "host: offline" when working on local machine[/+] on Aug 4, 2026
  2. changed the title [-]hosts_list / MCP tools return "host: offline" when working on local machine[/-] [+]hosts_list / MCP tools return "host: offline" when working on local host[/+] on Aug 4, 2026
  3. changed the title [-]hosts_list / MCP tools return "host: offline" when working on local host[/-] [+]hosts_list / MCP tools return "host: offline" when working on a local host[/+] on Aug 4, 2026
  4. github-actions commented on Aug 4, 2026

    @github-actions
    Contributor

    Summary

    The report is accurate, and the code confirms both halves of it: the behavior is intentional, and there is currently no signal anywhere in the cloud data model, API, CLI, or MCP surface that distinguishes "this host's relay is off by local setting" from "this host is down." v2_hosts.is_online is a pure relay-presence bit, and the setting that gates the relay (expose_host_service_via_relay) lives only in the host machine's local SQLite and is never reported upward — so the two states are genuinely indistinguishable to any remote consumer, not merely under-labelled.

    Affected code

    The gate itself:

    • apps/desktop/src/main/lib/host-service-coordinator.ts:769-770 reads settings.exposeHostServiceViaRelay from the local DB, and :821-826 deletes RELAY_URL from the child env when it's off (explicitly re-deleting it after the shell-env merge).
    • Default is false: apps/desktop/src/shared/constants.ts:59 (DEFAULT_EXPOSE_HOST_SERVICE_VIA_RELAY), which matches the NULL column you found.
    • packages/host-service/src/serve.ts:106-115 only calls connectRelay when env.RELAY_URL is set.
    • Notably, host.ensure — cloud host registration — lives inside that same relay path (packages/host-service/src/tunnel/connect.ts:20), and it's the only production caller (packages/trpc/src/router/host/host.ts:79-90 is the only insert(v2Hosts) in the repo). So a relay-disabled host never re-registers or refreshes its row either; it only appears in hosts_list at all because it registered during some earlier period when relay was on.

    Where online comes from:

    • apps/relay/src/tunnel.ts:315 — the relay is the only writer of host.setOnline, on tunnel open/close.
    • apps/api/src/app/api/hosts/jobs/sync-presence/route.ts:91-106 — the reconcile job derives is_online purely from the relay directory zset.
    • packages/db/src/schema/schema.ts:446-473 — v2_hosts has is_online, wake_command, names, timestamps. There is no relay-enablement column.

    Where it surfaces:

    • packages/trpc/src/router/host/host.ts:54-60 — host.list returns { id, name, online, wakeCommand, organizationId }, no reason field.
    • packages/mcp/src/tools/hosts/list.ts:13 forwards host.list verbatim; packages/sdk/src/resources/hosts.ts:31-37 mirrors the same shape; packages/cli/src/commands/hosts/list/command.ts:29 renders it.
    • packages/cli/src/commands/status/command.ts reports local manifest/pid/health only and never mentions relay — which is exactly why restarting looks like a silent no-op.

    Root cause

    Two independent things collapse onto one boolean:

    1. exposeHostServiceViaRelay is host-local state (packages/local-db/src/schema/schema.ts, exposed only over Electron IPC at apps/desktop/src/lib/trpc/routers/settings/index.ts:646-680) that is never transmitted to the cloud.
    2. is_online is written exclusively by the relay as a function of tunnel presence.

    So relay-off ⇒ no RELAY_URL ⇒ no tunnel ⇒ is_online stays false permanently, and restarting host-service can't change it because buildEnv re-reads the same setting on every spawn. There is no bit in the cloud schema that could carry the distinction even if host.list wanted to report it — any fix needs the host to report relay-enablement upward (see below).

    One correction to the report, since it affects where a fix belongs: host-scoped MCP tools do not hang or silently fail — they fail fast with a 503. apps/relay/src/index.ts:215-217 returns a tRPC SERVICE_UNAVAILABLE / "Host is not online" for /trpc paths (apps/relay/src/index.ts:210 returns {"error":"Host not connected"} for non-tRPC), packages/mcp/src/host-service-client.ts:46-50 wraps that into Host <id> returned 503 for <procedure>: …, and packages/mcp/src/define-tool.ts:137-150 returns it as isError: true. The maybeReplay fly-replay path (:120-149) returns null when the directory has no owner, so there's no waiting. The agent therefore does get "Host is not online" — it just gets no hint that the cause is a local opt-in setting rather than a dead machine. If you actually observed a hang (rather than a fast unhelpful error), that's a distinct bug worth its own report with the tool name and duration.

    Worth noting there's already precedent for the fix you're asking for, on the one surface that can read the local setting: apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/components/RelayOfflineNotice/RelayOfflineNotice.tsx:40-75 renders "This device isn't connected to the Superset relay… Enable relay access…" — but it infers this from online === false for the local host id, a shortcut unavailable to CLI/SDK/MCP callers. Similarly, superset hosts list already prints local instead of no for the local machine (command.ts:29), which is itself evidence that the online column is known to be ambiguous here.

    Reproduction

    1. Desktop → Settings → Security → turn off Allow remote workspaces to access this device via relay (or start from a fresh profile, where the column is NULL). The mutation restarts host-service (apps/desktop/src/lib/trpc/routers/settings/index.ts:653-680).
    2. superset hosts list → that machine shows local; host.list / hosts_list → online: false, no reason.
    3. Call workspaces_list / terminals_list with that hostId → Host <id> returned 503 … "Host is not online".
    4. Restart the app, or tray → Host Service → Restart → state is unchanged, because buildEnv re-reads the setting each spawn.
    5. Confirm the local CLI at http://127.0.0.1:<port> still works (your workaround) — it bypasses the relay entirely.

    Unit-level sketch: apps/desktop/src/main/lib/host-service-coordinator.test.ts currently has no coverage of the RELAY_URL gate (no match for RELAY_URL, exposeHostServiceViaRelay, or buildEnv). A bun:test case that stubs the settings row as { exposeHostServiceViaRelay: null } with RELAY_URL present in process.env, calls spawn, and asserts childEnv.RELAY_URL === undefined would pin the current behavior and give the fix a regression test.

    Suggested fix direction

    The minimal version of suggestion (1) requires a schema change, because the cloud has nowhere to put the answer:

    1. Add a relay_enabled (or relay_opt_in) column to v2_hosts and have the host report it on every host-service start — not only from connectRelay. Concretely: pass the setting into buildEnv as an explicit env var (rather than encoding it as "RELAY_URL present or absent"), and move the host.ensure call in packages/host-service/src/tunnel/connect.ts:20 out to an unconditional startup step in serve.ts that sends relayEnabled. That also fixes the related gap that relay-disabled hosts never refresh their cloud row at all. Then host.list can return { online, relayEnabled, lastSeenAt } and both CLI and hosts_list can render "off by choice" distinctly.
    2. Cheaper, no-migration interim: extend the relay's 503 detail and the hostServiceCall error text to say why a host may be unreachable (Host is not online — the host must be running and have relay access enabled in Settings → Security on that device, apps/relay/src/index.ts:216 + packages/mcp/src/host-service-client.ts:46-50), and add the same sentence to the host-scoped MCP tool descriptions (workspaces_*, terminals_*, agents_*, projects_list) and to apps/docs/content/docs/mcp-server.mdx, which currently never mentions the relay.
    3. For suggestion (2): superset status (packages/cli/src/commands/status/command.ts) already reads the local manifest and is the natural place to print relay state and the enable path; it could read the desktop setting or the presence of the relay tunnel and add a line like relay: disabled (Settings → Security, or 'superset start --daemon' on a dedicated host).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions