npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ao-mcp-bridge

v1.0.4

Published

stdio ↔ HTTP bridge for Claude Desktop ↔ ao-mcp-server with OAuth 2.1 + PKCE + Dynamic Client Registration. Single-file, zero runtime dependencies.

Downloads

740

Readme

ao-mcp-bridge

Single-file stdio ↔ HTTP bridge for connecting Claude Desktop to ao-mcp-server. Supports API-key mode (default) and OAuth 2.1 + PKCE + Dynamic Client Registration (opt-in via --oauth).

                       ┌─────────────────────────────────────┐
Claude Desktop  ──stdio──▶│  ao-mcp-bridge (this package)    │──HTTP──▶  ao-mcp-server (:8080)
   (JSON-RPC)             │  • Bearer / API-key forwarding   │              │
                          │  • OAuth flow on 401             │              ▼
                          │  • Server→client SSE notifs      │       ao-mcp-security-service (:9000)
                          │  • DELETE /mcp on shutdown       │              │
                          └─────────────────────────────────────┘              ▼
                                                                          AO Platform (:8090)

Table of contents


What's new in 1.0.3

Production-hardening release. Fully backwards-compatible defaults — no flag changes required to upgrade from 1.0.2. New behavior is opt-in via new flags; bug fixes trigger only in failure paths 1.0.2 already handled less gracefully. See CHANGELOG.md for full detail.

Fixed

  • tools/call failures now reach the Claude Desktop tool-call UI (returned as result.isError = true per MCP spec instead of a top-level JSON-RPC error that the client renders as blank). Non-tools/call failures still use top-level JSON-RPC errors — that's the correct channel for those.
  • Non-JSON / non-SSE 200 responses (e.g., HTML maintenance page) no longer silently drop and hang the client; the bridge surfaces a clear error.
  • --debug body logging now redacts JSON / form-urlencoded fields matching token / secret / password / credential / authorization / api_key / code / *_token / client_secret. Headers were already redacted in 1.0.2.

Added

  • HTTPS hardening: --ca-file, --client-cert + --client-key (mTLS), --require-https (fail-closed).
  • Cancellation: notifications/cancelled aborts the matching upstream POST immediately so the platform stops working on a result the client no longer wants.
  • Concurrent forwards: a long Semantic SQL / NLQ tool call no longer blocks other tool calls or notifications.
  • Bounded retry on 502/503/504 (3 attempts, 500 ms / 1000 ms backoff).
  • --max-body-bytes (default 50 MB) — OOM guard against pathological responses.
  • --log-format json — one structured event per stderr line for fleet log aggregation. Events: startup, request, response, auth_401, retry_5xx, cancel_in_flight, sse_open, sse_error, sse_giveup, teardown_abort, each with stable fields (mcp_method, mcp_id, latency_ms, etc.).

Changed

  • License: UNLICENSEDApache-2.0. No code change; clarifies usage rights for the public-registry distribution.

Why this bridge exists

Existing bridges fail against ao-mcp-security-service's ephemeral proxy-{uuid} client pattern, or simply lack OAuth support:

| Bridge | Failure mode | | --- | --- | | [email protected] | Stale lockfile wedges callback port 38351 across sessions → "site can't be reached" | | mcp-remote@latest | DCR client-info persistence bug → Existing OAuth client information is required when exchanging an authorization code | | supergateway | No OAuth — exits on first 401 | | Claude Desktop native remote MCP | Gated off in current enterprise build |

This bridge is ~600 lines of Node with zero runtime dependencies, engineered specifically to avoid those failure modes.


Features

  • Fresh random callback port every OAuth flow — no lockfile, no cross-session coordination
  • Persisted DCR client info keyed by auth-server issuer (survives restarts)
  • Auto re-auth on 401 — initial and mid-session, including platform 401s propagated by PlatformUnauthorizedPropagationFilter
  • Refresh-token support — silent token rotation, no browser prompt when access token expires
  • Streamable HTTP — POST /mcp with JSON or SSE responses
  • Long-lived GET /mcp SSE — server→client tools/list_changed, etc. reach Claude Desktop
  • DELETE /mcp on shutdown — clean server-side session teardown
  • Header injection--header (repeatable) for X-Platform-Api-Key, tracing IDs, tenant routing, etc. OAuth's Authorization always wins over user-supplied auth headers
  • stderr-only logging — keeps stdout clean for JSON-RPC framing; --debug adds verbose request/response with secrets redacted

Requirements

| For… | Need | | --- | --- | | Running the bridge from source | Node ≥ 20.6 | | Running via npx from npm | Node ≥ 20.6 (npx ships with npm) | | Running the standalone .exe | Nothing — Node runtime is embedded | | Building the standalone .exe | Node ≥ 20.6 (host OS == target OS — no cross-compile) | | Publishing to npm | Node ≥ 20.6, npm account, write access to the package name |


Distribution options

Three ways to ship the bridge to end users — pick the one that fits your environment:

| Option | End user needs | Install effort | Best for | | --- | --- | --- | --- | | A. npx + npm | Node | Zero — fetches on first run | Most end users; auto-updates if pinned to a range | | B. Local checkout | Node + git clone | Manual | Developers / debugging | | C. Standalone .exe | Nothing | Copy the file | Locked-down machines without Node |

Each option corresponds to a Claude Desktop configuration mode below.


Platform paths reference

The Modes below use placeholders like <bridge-path>. Substitute the right value for your operating system:

Claude Desktop config file

| OS | Path | | --- | --- | | Windows | %APPDATA%\Claude\claude_desktop_config.json | | macOS | ~/Library/Application Support/Claude/claude_desktop_config.json | | Linux | ~/.config/Claude/claude_desktop_config.json |

<bridge-path> — absolute path to mcp-bridge.mjs (Modes 1–4 only)

| OS | Example | | --- | --- | | Windows | C:\\path\\to\\ao-mcp-server\\scripts\\mcp-bridge.mjs (note: backslashes must be escaped in JSON) | | macOS / Linux | /path/to/ao-mcp-server/scripts/mcp-bridge.mjs |

<exe-path> — absolute path to the built executable (Mode 6 only)

| OS | Example | | --- | --- | | Windows | C:\\Tools\\mcp-bridge.exe | | macOS / Linux | /usr/local/bin/mcp-bridge |

Bridge state directory

| OS | Path | | --- | --- | | Windows | %USERPROFILE%\.ao-mcp-bridge\ | | macOS / Linux | ~/.ao-mcp-bridge/ |

Claude Desktop logs

| OS | Path | | --- | --- | | Windows | %APPDATA%\Claude\logs\mcp-server-ao-mcp-server.log | | macOS | ~/Library/Logs/Claude/mcp-server-ao-mcp-server.log | | Linux | ~/.config/Claude/logs/mcp-server-ao-mcp-server.log |


Claude Desktop configuration

Edit the config file at the path for your OS. Restart Claude Desktop after editing.

Always pass --timeout 600 (10 minutes) so long-running Semantic SQL / NLQ tool calls don't hit the bridge's default 60 s POST timeout. Every example includes it.

Quick reference

| Mode | OAuth | Headers | Distribution | Use case | |---|---|---|---|---| | 1 | ✓ | — | Local checkout | Pure SSO. First launch opens browser; tokens cached. | | 2 | — | X-Platform-Api-Key | Local checkout | Static credential, no browser. | | 3 | ✓ | tracing / tenant / region | Local checkout | SSO with operational headers. | | 4 | ✓ | X-Platform-Api-Key | Local checkout | API-key by default; OAuth as backstop. | | 5 | any | any | npm registry | End users — no clone, no PATH setup. | | 6 | any | any | Pre-built .exe | Machines without Node. |


Mode 1 — OAuth-only (SSO)

{
  "mcpServers": {
    "ao-mcp-server": {
      "command": "node",
      "args": [
        "<bridge-path>",
        "http://127.0.0.1:8080/mcp",
        "--oauth",
        "--timeout", "600"
      ]
    }
  }
}

First launch runs discovery → DCR → PKCE, opens your browser. Tokens are cached in the bridge state directory; subsequent launches skip the prompt. Mid-session 401 (token expired and refresh failed) re-runs the browser flow automatically.


Mode 2 — API-key only

{
  "mcpServers": {
    "ao-mcp-server": {
      "command": "node",
      "args": [
        "<bridge-path>",
        "http://127.0.0.1:8080/mcp",
        "--header", "X-Platform-Api-Key: <YOUR_API_KEY>",
        "--timeout", "600"
      ]
    }
  }
}

API key on every request. No browser ever opens. On 401, surfaces an error to Claude Desktop:

  • tools/call failures arrive as a successful result with isError: true and the message in content[0].text (so the tool-call UI displays the reason).
  • Non-tools/call failures (e.g., tools/list) arrive as a top-level JSON-RPC error.

Mode 3 — OAuth + custom headers

{
  "mcpServers": {
    "ao-mcp-server": {
      "command": "node",
      "args": [
        "<bridge-path>",
        "http://127.0.0.1:8080/mcp",
        "--oauth",
        "--header", "X-Request-Id: claude-desktop",
        "--header", "X-Tenant-Id: acme",
        "--header", "X-Region: us-east-1",
        "--timeout", "600"
      ]
    }
  }
}

OAuth provides the Authorization: Bearer <jwt>; --header adds non-auth headers (tracing, tenant routing, region pinning). The fresh OAuth Bearer is applied after --header, so a stale or accidental --header "Authorization: ..." cannot stomp the live token.

--header is repeatable. Names are logged at startup; values are never logged.


Mode 4 — Mixed (API-key with OAuth fallback)

{
  "mcpServers": {
    "ao-mcp-server": {
      "command": "node",
      "args": [
        "<bridge-path>",
        "http://127.0.0.1:8080/mcp",
        "--header", "X-Platform-Api-Key: <YOUR_API_KEY>",
        "--oauth",
        "--timeout", "600"
      ]
    }
  }
}

Sends the API key by default. If the platform rejects it (revoked, expired entitlement, etc.) falls back to OAuth and opens the browser.


Mode 5 — npx (zero-install end-user setup)

End users don't need to clone the repo. npx fetches the package from the npm public registry on first run and caches it.

{
  "mcpServers": {
    "ao-mcp-server": {
      "command": "npx",
      "args": [
        "-y", "ao-mcp-bridge@latest",
        "http://127.0.0.1:8080/mcp",
        "--oauth",
        "--timeout", "600"
      ]
    }
  }
}

@latest resolves to the newest published bridge on each npx run (subject to npm cache). For a fixed install, use [email protected] instead. The npx cache location varies by OS:

| OS | npx cache | | --- | --- | | Windows | %LOCALAPPDATA%\npm-cache\_npx\ | | macOS / Linux | ~/.npm/_npx/ |

If Claude Desktop can't find npx on PATH, expand command to the full path:

| OS | Typical full path | | --- | --- | | Windows | C:\\Program Files\\nodejs\\npx.cmd | | macOS (Homebrew) | /opt/homebrew/bin/npx (Apple Silicon) or /usr/local/bin/npx (Intel) | | Linux | /usr/bin/npx or /usr/local/bin/npx |

All --header, --oauth, etc. flags work identically — flags pass through npx to the bridge.


Mode 6 — Standalone .exe (no Node on target machine)

After building the executable, distribute the binary and point the config directly at it:

{
  "mcpServers": {
    "ao-mcp-server": {
      "command": "<exe-path>",
      "args": [
        "http://127.0.0.1:8080/mcp",
        "--oauth",
        "--timeout", "600"
      ]
    }
  }
}

The binary embeds the Node runtime — no install needed on the target machine. ~80 MB on disk.


Updating to a new version

When you change the bridge spec in your config (e.g. switch from a pinned version to @latest, or bump 1.0.21.0.3), npx may serve the previously-cached package and Claude Desktop may keep a stale child process running. To force a clean re-fetch:

| What you're clearing | Why | When you need it | | --- | --- | --- | | Claude Desktop process | Avoid an old child bridge process surviving a bridge version or spec change | After every bridge upgrade (Mode 5 + Mode 6) | | npx package cache | Forces re-download of the requested bridge tag from the npm registry | After changing @latest / pinned version in Mode 5 when cache serves stale code | | Bridge state dir (~/.ao-mcp-bridge/) | Wipes DCR client + tokens — next launch re-runs OAuth from scratch | Only when troubleshooting auth (401, expired refresh token, auth-server changed). Skip otherwise — the bridge handles re-auth automatically | | Legacy ~/.mcp-auth/ | Cleanup if migrating from mcp-remote | Once during initial migration; safe to skip thereafter |

Windows (PowerShell)

# Stop Claude Desktop (including any background/tray processes)
Get-Process "Claude*" -ErrorAction SilentlyContinue | Stop-Process -Force

# Required after a Mode 5 (npx) version bump:
Remove-Item -Recurse -Force "$env:LOCALAPPDATA\npm-cache\_npx\*"   -ErrorAction SilentlyContinue

# Optional — only if re-auth is also needed:
Remove-Item -Recurse -Force "$env:USERPROFILE\.ao-mcp-bridge\*"    -ErrorAction SilentlyContinue

# Legacy mcp-remote cache — only if migrating from mcp-remote:
Remove-Item -Recurse -Force "$env:USERPROFILE\.mcp-auth\*"         -ErrorAction SilentlyContinue

# Re-launch Claude Desktop manually

macOS

# Stop Claude Desktop
pkill -f "Claude" 2>/dev/null || true

# Required after a Mode 5 (npx) version bump:
rm -rf ~/.npm/_npx/*

# Optional — only if re-auth is also needed:
rm -rf ~/.ao-mcp-bridge/*

# Legacy mcp-remote cache — only if migrating from mcp-remote:
rm -rf ~/.mcp-auth/*

# Re-open from Applications or:
open -a "Claude"

Linux

# Stop Claude Desktop
pkill -f "claude-desktop\|Claude" 2>/dev/null || true

# Required after a Mode 5 (npx) version bump:
rm -rf ~/.npm/_npx/*

# Optional — only if re-auth is also needed:
rm -rf ~/.ao-mcp-bridge/*

# Legacy mcp-remote cache — only if migrating from mcp-remote:
rm -rf ~/.mcp-auth/*

# Re-launch Claude Desktop

Quick verification

After cleanup, force a fresh fetch and confirm the new version resolves:

# All platforms
npx -y ao-mcp-bridge@<new-version> --help

The first line of stderr should print ao-mcp-bridge <new-version> starting → ... confirming the new tarball is in use.


CLI flag reference

| Flag | Purpose | | --- | --- | | --header "Name: Value" (repeatable) | Adds header to every outgoing HTTP request. OAuth Bearer always overrides any --header "Authorization: ...". | | --oauth | Enables OAuth/PKCE flow on 401. Default: off. Without it, 401 surfaces as a JSON-RPC error. | | --timeout <seconds> | Per-request POST timeout. Default: 60. Pass --timeout 600 for long-running Semantic SQL / NLQ. SSE streams are exempt. | | --allow-http | Allow non-loopback HTTP URLs. Default: only 127.0.0.1 / localhost HTTP allowed; HTTPS always allowed. Mutually exclusive with --require-https. | | --require-https | Fail-closed: refuse any http:// URL (even loopback). For production deployments where a misconfigured URL must not leak credentials in plaintext. | | --ca-file <path> | PEM bundle for custom / private-CA truststore. Used for HTTPS upstreams whose cert isn't anchored in the system trust store. | | --client-cert <path> --client-key <path> | mTLS client certificate + private key (PEM). Must be supplied together. | | --max-body-bytes <n> | Cap on buffered POST response body size. Default: 52428800 (50 MB). Exceeded responses surface as a clear error instead of OOMing the bridge. SSE streams are exempt (not buffered). | | --log-format text\|json | text (default — human-readable lines, identical to 1.0.2). json emits one structured event per stderr line for fleet log aggregation. | | --transport <flavor> | http-only (default) / http-first (alias) / sse-first (warns + falls through) / sse-only (errors — legacy SSE not implemented). | | --static-oauth-client-info '{...}' | Skip DCR; use a pre-registered client_id ({"client_id": "...", "client_secret": "..."}). | | --static-oauth-client-metadata '{...}' | Override DCR registration body fields. redirect_uris is always re-applied to the bound port. | | --auth-only | Run discovery + OAuth + token exchange, then exit. Implies --oauth. Useful for debugging the auth pipeline. | | --debug | Verbose request/response logging on stderr. Headers AND body fields matching token / secret / password / credential / authorization / api_key / code / *_token / client_secret are redacted. | | --clean | Delete the bridge state directory and exit 0. |


Persisted state

All under the bridge state directory:

| File | Contents | When to delete | | --- | --- | --- | | client.json | DCR registration: client_id, client_secret, auth_server, redirect_uri | Auth server regenerated its client DB | | tokens.json | access_token, refresh_token, expires_at | Tokens stale/bad (the bridge clears these on 401 automatically) |

Both are rewritten as needed. Manual cleanup: run the bridge with --clean.


Logs & debugging

Logs go to stderr → captured by Claude Desktop into the Claude Desktop log file. Add --debug for full request/response visibility (headers AND body fields redacted — see CLI flag reference).

Text mode (default)

Key lines to look for:

ao-mcp-bridge 1.0.3 starting → http://127.0.0.1:8080/mcp
OAuth: enabled (--oauth)         | OAuth: disabled (default; pass --oauth to enable)
Custom headers: X-Platform-Api-Key
Fetching resource metadata: ...                         (RFC 9728 discovery, OAuth path)
DCR: registering client at ... for redirect_uri = ...   (or "Using --static-oauth-client-info")
Callback listener bound on http://127.0.0.1:<port>      (fresh port per flow)
Access token obtained (expires_in = ... sec)
Opening notifications SSE stream (attempt 1)
MCP returned 401 (attempt 1) ...                        (re-auth trigger)
Retrying after 500 ms (5xx attempt 2 of 3)              (transient gateway retry)
Cancelled in-flight request id = 7                      (notifications/cancelled propagation)
Sent DELETE /mcp for session <id>                       (clean shutdown)

JSON mode (--log-format json)

Each line is one structured event suitable for fleet log aggregation. Stable shape: { "ts": ISO-8601, "level": "info", "event": "...", ...fields }. Example trace of a single tool call returning 401:

{"ts":"2026-05-01T21:30:14Z","level":"info","event":"startup","version":"1.0.3","mcp_url":"http://127.0.0.1:8080/mcp","oauth":false,"transport":"http-only","timeout_ms":600000,"max_body_bytes":52428800,"log_format":"json","custom_ca":false,"mtls":false,"require_https":false}
{"ts":"2026-05-01T21:30:15Z","level":"info","event":"request","mcp_method":"tools/call","mcp_id":4,"body_bytes":83}
{"ts":"2026-05-01T21:30:15Z","level":"info","event":"auth_401","mcp_method":"tools/call","mcp_id":4,"attempt":1,"oauth_enabled":false}
{"ts":"2026-05-01T21:30:15Z","level":"info","event":"response","mcp_method":"tools/call","mcp_id":4,"status":401,"latency_ms":108,"body_bytes":89}

Other event names: response_5xx, response_non_json, retry_5xx, cancel_in_flight, sse_open, sse_error, sse_giveup, sse_ended_cleanly, teardown_abort. All carry mcp_method and mcp_id where relevant.

Standalone debugging — drive OAuth without launching Claude Desktop:

# macOS / Linux
node mcp-bridge.mjs http://127.0.0.1:8080/mcp --oauth --auth-only --debug
# Windows
node mcp-bridge.mjs http://127.0.0.1:8080/mcp --oauth --auth-only --debug

For maintainers — Publishing to npm

The package is configured in scripts/package.json:

| Field | Value | | --- | --- | | name | ao-mcp-bridge (unscoped) | | bin | ao-mcp-bridgemcp-bridge.mjs | | files | mcp-bridge.mjs, README.md, LICENSE, CHANGELOG.md (4 files) | | engines.node | >=20.6.0 | | scripts.test | node --test mcp-bridge.test.mjs | | scripts.check-version-sync | Asserts the VERSION constant in mcp-bridge.mjs matches package.json's version. Fails publish if drifted. | | scripts.prepublishOnly | npm run check-version-sync && node --check ./mcp-bridge.mjs && npm test |

Two version fields, must stay in sync. The bridge advertises its version on stderr at startup (ao-mcp-bridge 1.0.4 starting → ...), which means the literal string is duplicated in mcp-bridge.mjs (const VERSION = '...') alongside package.json's version field. npm version only edits package.json — you must hand-edit the VERSION constant or the check-version-sync script fails the publish.

One-time setup

cd ao-mcp-server/scripts

# Confirm name is available (skip if you already publish this package)
npm view ao-mcp-bridge

# If taken, switch to a scoped name in package.json (requires the org on npm):
#   "name": "@your-org/mcp-bridge"

# Authenticate. Two options:
#   A. Interactive: npm login   (stores token in ~/.npmrc on macOS/Linux,
#                                or %USERPROFILE%\.npmrc on Windows)
#   B. CI / scripted: set NPM_TOKEN env var. The repo already ships a project-local
#      .npmrc at scripts/.npmrc containing:
#         //registry.npmjs.org/:_authToken=${NPM_TOKEN}
#      so `npm publish` picks it up automatically. Keep .npmrc gitignored if you ever
#      put a literal token in it (the templated form above is safe to commit).

Publish (per-release)

cd ao-mcp-server/scripts

# 1. Bump version in package.json. npm version refuses to run if the working tree
#    is dirty — commit or stash unrelated changes first, or pass --allow-same-version
#    only on intentional re-tags.
npm version patch  --no-git-tag-version    # 1.0.3 → 1.0.4   (bug fix / recovery)
# or  npm version minor --no-git-tag-version    # 1.0.3 → 1.1.0   (additive feature)
# or  npm version major --no-git-tag-version    # 1.0.3 → 2.0.0   (breaking change)

# 2. Hand-edit the matching VERSION constant in mcp-bridge.mjs (line ~66):
#       const VERSION = 'X.Y.Z';
#    Skipping this step makes prepublishOnly fail with "Version mismatch".

# 3. Update CHANGELOG.md — add a new section above the previous one, following
#    the Keep a Changelog format already used in the file (Fixed / Added / Changed).

# 4. Smoke-test locally. These three commands run automatically inside prepublishOnly,
#    but running them by hand catches issues without leaving the registry in a half
#    state on failure.
npm run check-version-sync                  # VERSION constant matches package.json
node --check ./mcp-bridge.mjs               # parse syntax
npm test                                    # node --test mcp-bridge.test.mjs

# 5. Inspect the tarball file list — should be exactly 4 files.
npm pack --dry-run

# 6. Publish. publishConfig.access:public in package.json handles the unscoped public
#    registry case. With NPM_TOKEN set, no interactive 2FA prompt for an Automation token.
npm publish

# Scoped packages (@scope/name) are private by default — force public:
#   npm publish --access public

# 7. Commit + tag. Use the ao-mcp-bridge- prefix on the tag so bridge releases are
#    distinguishable from any future ao-mcp-server release tags in the same monorepo.
cd ../..
git add ao-mcp-server/scripts/package.json ao-mcp-server/scripts/mcp-bridge.mjs ao-mcp-server/scripts/CHANGELOG.md
git commit -m "ao-mcp-bridge X.Y.Z: <one-line summary>"
git tag ao-mcp-bridge-vX.Y.Z
git push origin <branch> --follow-tags

Required token type: Automation (or Granular with publish + write permission for the package). Read-only tokens fail with 403 You may not perform that action with these credentials.

Post-publish verification

# The new version is visible on the registry within a few seconds.
npm view [email protected] version dist.tarball

# End-to-end: fresh fetch via npx prints the new version on the first stderr line.
npx -y [email protected] --help

Versioning hygiene

  • Version spec: Docs use ao-mcp-bridge@latest for convenience. For strict reproducibility in production, pin [email protected] and bump deliberately; @latest can change when the package is published.
  • unpublish window: 72 hours from publish. Note that the same version cannot be republished for 24 hours after unpublishing — bump to the next patch and republish instead. After 72 hours, unpublish is no longer permitted; deprecate instead: npm deprecate ao-mcp-bridge@<bad-version> 'use <good-version>'.
  • CI: a one-line GitHub Action on tag push (npm publish) keeps releases consistent. Out of scope here.

Common pitfalls

| Symptom | Cause | Fix | | --- | --- | --- | | prepublishOnly fails with Version mismatch: package.json=X.Y.Z, mcp-bridge.mjs=A.B.C | Forgot Step 2 — npm version updates package.json only | Edit the VERSION constant in mcp-bridge.mjs to match, then re-run npm publish. | | npm publish exits with 403 You do not have permission to publish "ao-mcp-bridge" | Token lacks publish scope, or you're not a maintainer of that package name | Use an Automation/Granular token with publish permission, or coordinate with the npm org owner. | | npm publish exits with 403 Cannot publish over previously-published version X.Y.Z | Version not bumped, or you republished after a same-version unpublish within the 24 h embargo | Bump to the next patch (Step 1) and republish. | | npx -y ao-mcp-bridge@latest keeps serving the previous version after publish | npx cache | Clear ~/.npm/_npx/ (macOS / Linux) or %LOCALAPPDATA%\npm-cache\_npx\ (Windows). See Updating to a new version. |


For maintainers — Building the standalone executable

For machines that can't install Node, build a self-contained binary using Node's official Single Executable Application (SEA) feature:

# From the ao-mcp-server directory
node scripts/build-exe.mjs

Output:

| Build host | Output path | | --- | --- | | Windows | dist\mcp-bridge.exe | | macOS | dist/mcp-bridge | | Linux | dist/mcp-bridge |

~80 MB — embeds the build host's Node runtime.

Pipeline

scripts/build-exe.mjs runs four steps:

  1. esbuild — bundles ESM (mcp-bridge.mjs) → CommonJS. Required because Node SEA only supports ESM directly on Node 22.5+; CJS keeps the build portable down to Node 20.
  2. Node SEA — generates a binary blob from the CJS bundle.
  3. Copy the host's node binary to dist/mcp-bridge[.exe] as the runtime base.
  4. postject injects the SEA blob into the copied executable, replacing the sentinel fuse the Node runtime looks for at startup.

esbuild and postject are auto-fetched via npx --yes — no npm install needed.

Cross-compilation

Not supported by Node SEA. Build on the platform you target:

| Build host | Output | Post-build | | --- | --- | --- | | Windows | mcp-bridge.exe | Optional: signtool sign /fd SHA256 dist\mcp-bridge.exe to avoid SmartScreen warnings. | | macOS | mcp-bridge | Required: codesign --sign - dist/mcp-bridge (ad-hoc) or with a Developer ID. | | Linux | mcp-bridge | None. chmod +x already applied by build script. |

Smoke test

# macOS / Linux
dist/mcp-bridge --help
dist/mcp-bridge http://127.0.0.1:8080/mcp --oauth --auth-only --debug
# Windows
dist\mcp-bridge.exe --help
dist\mcp-bridge.exe http://127.0.0.1:8080/mcp --oauth --auth-only --debug

Caveats

  • Output size: ~80 MB (embedded Node runtime).
  • postject warning "The signature seems corrupted!" — benign on Windows; refers to the Authenticode signature on the Node binary, invalidated by blob injection. Re-sign with signtool if shipping to end users.
  • Node version match: the embedded runtime is whichever Node you ran the build with. Build with the version you've validated.

Known limitations

  • No legacy SSE transport (separate /sse + /messages endpoints). ao-mcp-server uses STREAMABLE HTTP. If ever needed, see the comment near --transport validation in mcp-bridge.mjs.
  • No env-var / config-file based settings. CLI flags only, by design.
  • No lockfile-based port coordination. Explicitly avoided — that's the bug in [email protected] that motivated this bridge.
  • OAuth user-login timeout: 5 minutes. After that the bridge emits a JSON-RPC error and the next request retries.
  • Single in-flight OAuth: concurrent stdio messages during an OAuth flow queue behind the same promise — one browser, one callback server, done.