Skip to content

Add install-beeper.sh: one-command Beeper to Edison MCP gateway installer#15

Merged
Miyamura80 merged 3 commits into
mainfrom
claude/relaxed-planck-dtn99w
Jul 25, 2026
Merged

Add install-beeper.sh: one-command Beeper to Edison MCP gateway installer#15
Miyamura80 merged 3 commits into
mainfrom
claude/relaxed-planck-dtn99w

Conversation

@Miyamura80

@Miyamura80 Miyamura80 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

What

Adds crates/stdiod/scripts/install-beeper.sh, an agent-friendly installer that wires Beeper into the Edison Watch MCP gateway on macOS. It was developed in the now-archived Edison-Watch/stdiod repo (PR #15 there, frozen by the archive); this ports the final, reviewed version into the monorepo.

What it does

Automates the Edison side end to end using the shipped device-authorization flow:

deps -> Beeper-Desktop check -> edison-stdiod login (browser device auth)
     -> edison-stdiod install (supervisor) -> server add (submit for approval)
     -> discover Beeper token -> bind-token (admin /env push)

Subcommands: install, doctor, status, token, bind-token, mcp-url, uninstall. Agent-friendly throughout: every input is a flag or UPPER_SNAKE env var, plus --dry-run, --yes, --json, --verbose, TTY-aware colors, and fail-fast errors that print the exact fix. --demo/--release shortcuts, and with neither set it follows the backend the device is already authorized to.

Steps that still need a human (printed as explicit action: lines)

Beeper serves MCP only from the Desktop app, and the device-auth server flow is HITL by design, so three steps are human-gated and the script guides each:

  1. Enable MCP in Beeper Desktop (Settings > Developers > MCP) and link chats there.
  2. Approve the submitted beeper server once in the dashboard (Servers page / Overview). A "not verified" badge before the token is set is expected and does not block approval.
  3. bind-token --ew-api-key <admin-key> pushes BEEPER_ACCESS_TOKEN via the admin /env route.

Upstream blocker: beeper_execute (does NOT block this PR)

The full Edison chain is proven end to end on a real Mac: device auth -> server approval -> token bind -> gateway proxy -> @beeper/desktop-mcp loads, and beeper_search_docs returns live data. The one tool that does not work is Beeper's own beeper_execute, and the cause is entirely upstream:

Nothing on the Edison side needs to change for this; the installer and gateway path are complete. This is noted so reviewers know the execute limitation is an upstream Beeper bug, not a gap in this script.

Known gaps surfaced during real-device testing (not fixed here)

  • The device-scoped server add declares no env, so the dashboard shows no field for BEEPER_ACCESS_TOKEN; bind-token pushes it via POST /servers/{name}/env instead. A cleaner fix would let server add declare env-var names so the value has a dashboard field.
  • @beeper/desktop-mcp targets :23373 with no base-URL override, so Beeper Desktop must be on the default port (a stray headless beeper server can bump it to :23374).

Test plan

🤖 Generated with Claude Code


Generated by Claude Code

Port the Beeper installer into the app monorepo (the standalone Edison-Watch/
stdiod repo was archived). Wires Beeper into the Edison Watch MCP gateway on
macOS via the shipped device-authorization flow: browser device login, submit
the beeper stdio server for approval, discover the Beeper token, and bind it
via the admin /env route (bind-token). Includes --demo/--release backend
shortcuts and follows the device's authorized backend by default.

Agent-friendly: flags/env for every input, --dry-run, fail-fast errors with the
exact fix. Passes crates/stdiod/scripts/check_ai_writing.sh and shellcheck.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found across 1 file

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="crates/stdiod/scripts/install-beeper.sh">

<violation number="1" location="crates/stdiod/scripts/install-beeper.sh:217">
P2: The one-command `install --install-deps --yes` flow still fails on machines without Rust because Cargo is neither provisioned nor checked with a usable setup fix. Handle `cargo` as a prerequisite before attempting this install.</violation>

<violation number="2" location="crates/stdiod/scripts/install-beeper.sh:238">
P2: Beeper on 23374–23378 is reported reachable, but spawned `@beeper/desktop-mcp` still connects only to 23373. Limit this check to 23373 or fail with the documented port-conflict fix before submitting a server that cannot connect.</violation>

<violation number="3" location="crates/stdiod/scripts/install-beeper.sh:479">
P2: Rerunning `install` while the `beeper` request is still pending can fail instead of recognizing the existing request. The duplicate detection searches the CLI output for response-body text that `edison-stdiod` deliberately redacts, so the pending-request path should be handled by an explicit status/list API or by exposing a reliable machine-readable result.</violation>

<violation number="4" location="crates/stdiod/scripts/install-beeper.sh:624">
P2: Tokens containing JSON-special characters cannot be bound reliably because the request body is assembled with raw string interpolation. Serialize `BEEPER_ACCESS_TOKEN` with a JSON encoder rather than `printf` substitution.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread crates/stdiod/scripts/install-beeper.sh Outdated
[ -n "$code" ] && [ "$code" != "000" ] && { printf '%s' "$BEEPER_API_URL"; return 0; }
fi
for h in 127.0.0.1 localhost "[::1]"; do
for p in 23373 23374 23375 23376 23377 23378; do

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Beeper on 23374–23378 is reported reachable, but spawned @beeper/desktop-mcp still connects only to 23373. Limit this check to 23373 or fail with the documented port-conflict fix before submitting a server that cannot connect.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/stdiod/scripts/install-beeper.sh, line 238:

<comment>Beeper on 23374–23378 is reported reachable, but spawned `@beeper/desktop-mcp` still connects only to 23373. Limit this check to 23373 or fail with the documented port-conflict fix before submitting a server that cannot connect.</comment>

<file context>
@@ -0,0 +1,747 @@
+    [ -n "$code" ] && [ "$code" != "000" ] && { printf '%s' "$BEEPER_API_URL"; return 0; }
+  fi
+  for h in 127.0.0.1 localhost "[::1]"; do
+    for p in 23373 23374 23375 23376 23377 23378; do
+      url="http://$h:$p"
+      code="$(curl -s -m 2 -o /dev/null -w '%{http_code}' "${url}${wk}" 2>/dev/null || true)"
</file context>

brew install --quiet node
ensure_tool edison-stdiod \
"run: cargo install --path crates/edison-stdiod (or re-run with --install-deps)" \
cargo install --path "$stdiod_src"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The one-command install --install-deps --yes flow still fails on machines without Rust because Cargo is neither provisioned nor checked with a usable setup fix. Handle cargo as a prerequisite before attempting this install.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/stdiod/scripts/install-beeper.sh, line 217:

<comment>The one-command `install --install-deps --yes` flow still fails on machines without Rust because Cargo is neither provisioned nor checked with a usable setup fix. Handle `cargo` as a prerequisite before attempting this install.</comment>

<file context>
@@ -0,0 +1,747 @@
+    brew install --quiet node
+  ensure_tool edison-stdiod \
+    "run: cargo install --path crates/edison-stdiod   (or re-run with --install-deps)" \
+    cargo install --path "$stdiod_src"
+  if [ "$DRY_RUN" -eq 1 ]; then
+    info "deps: preview only (nothing was installed)"
</file context>

Comment thread crates/stdiod/scripts/install-beeper.sh Outdated
local code
code="$(curl -s -o /dev/null -w '%{http_code}' -m 60 --connect-timeout 5 -X POST "$url" \
-H "Authorization: Bearer ${EW_API_KEY}" -H "Content-Type: application/json" \
--data "$(printf '{"env":{"BEEPER_ACCESS_TOKEN":"%s"}}' "$tok")" 2>/dev/null || true)"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Tokens containing JSON-special characters cannot be bound reliably because the request body is assembled with raw string interpolation. Serialize BEEPER_ACCESS_TOKEN with a JSON encoder rather than printf substitution.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/stdiod/scripts/install-beeper.sh, line 624:

<comment>Tokens containing JSON-special characters cannot be bound reliably because the request body is assembled with raw string interpolation. Serialize `BEEPER_ACCESS_TOKEN` with a JSON encoder rather than `printf` substitution.</comment>

<file context>
@@ -0,0 +1,747 @@
+  local code
+  code="$(curl -s -o /dev/null -w '%{http_code}' -m 60 --connect-timeout 5 -X POST "$url" \
+    -H "Authorization: Bearer ${EW_API_KEY}" -H "Content-Type: application/json" \
+    --data "$(printf '{"env":{"BEEPER_ACCESS_TOKEN":"%s"}}' "$tok")" 2>/dev/null || true)"
+  [ -z "$code" ] && code="000"
+  case "$code" in
</file context>

Comment thread crates/stdiod/scripts/install-beeper.sh Outdated
Comment thread crates/stdiod/scripts/install-beeper.sh
Comment thread crates/stdiod/scripts/install-beeper.sh
--command npx --arg=-y --arg="$MCP_PKG" 2>&1)" && rc=0 || rc=$?
printf '%s\n' "$out" | grep -viE '^[[:space:]]*$' >&2 || true
if [ "$rc" -ne 0 ]; then
if printf '%s' "$out" | grep -qiE 'already (exists|submitted|pending)|duplicate'; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Rerunning install while the beeper request is still pending can fail instead of recognizing the existing request. The duplicate detection searches the CLI output for response-body text that edison-stdiod deliberately redacts, so the pending-request path should be handled by an explicit status/list API or by exposing a reliable machine-readable result.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/stdiod/scripts/install-beeper.sh, line 479:

<comment>Rerunning `install` while the `beeper` request is still pending can fail instead of recognizing the existing request. The duplicate detection searches the CLI output for response-body text that `edison-stdiod` deliberately redacts, so the pending-request path should be handled by an explicit status/list API or by exposing a reliable machine-readable result.</comment>

<file context>
@@ -0,0 +1,747 @@
+        --command npx --arg=-y --arg="$MCP_PKG" 2>&1)" && rc=0 || rc=$?
+  printf '%s\n' "$out" | grep -viE '^[[:space:]]*$' >&2 || true
+  if [ "$rc" -ne 0 ]; then
+    if printf '%s' "$out" | grep -qiE 'already (exists|submitted|pending)|duplicate'; then
+      ok "a request for '$SERVER_NAME' is already pending; approve it in the dashboard"
+      return 0
</file context>

Comment thread crates/stdiod/scripts/install-beeper.sh Outdated
Real end-to-end test surfaced two things:
- @beeper/desktop-mcp runs its `execute` tool (the main Beeper surface) in a
  local Deno sandbox, so Deno is a hard runtime dep. Add it to ensure_deps and
  doctor.
- The prior "headless Beeper has no MCP, Desktop app required" note was wrong.
  @beeper/desktop-mcp is the MCP bridge over Beeper's local Client API, which a
  headless `beeper` server serves too (verified against a headless server on a
  real Mac). Correct the header and the beeper-reachability guidance.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 1 file (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread crates/stdiod/scripts/install-beeper.sh
…JSON, dry-run)

Fixes the substantive findings from the cubic review on the PR:

- P1 security: restrict every URL that receives a Beeper bearer token to a
  loopback host. A hostile BEEPER_API_URL or a userinfo_endpoint injected into
  the well-known document could otherwise send each scraped token candidate to
  a remote server. New is_loopback_url guard; non-loopback overrides are
  ignored and a non-loopback userinfo_endpoint falls back to the local default.
- An EW_BACKEND supplied via the environment now sets EW_BACKEND_SET, so
  resolve_backend no longer silently overrides it with the device's saved
  session (could point install/bind-token at the wrong environment).
- JSON safety: --json output and the bind-token request body now escape
  interpolated values, so tokens/labels/backends containing quotes,
  backslashes, or newlines stay valid JSON.
- --dry-run is treated as non-mutating: `uninstall --dry-run` previews instead
  of refusing, and `bind-token --dry-run` no longer requires an admin key
  before the preview branch.
- doctor and status now report the Beeper "Client API" and mention the headless
  server option, matching the headless support added earlier.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CRjwx1BeWkTiS97Acyjvx
@Miyamura80
Miyamura80 merged commit 3669fd4 into main Jul 25, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants