pb-hypernode-mcp
Enables AI assistants to create, sanitize, and manage disposable Hypernode Brancher preview environments cloned from production, with automatic PII anonymization, credential reset, and sandbox-forcing before any access URL is revealed.
README
pb-hypernode-mcp
Client-side Claude Code plugin for Hypernode Brancher — spin up disposable, prod-clone preview environments, drive AI-assisted changes over SSH, view via your existing browser MCP.
Why
Brancher gives you a mutable, temporary copy of your production Hypernode (≤24h-old data, full toolchain, real infra — not a Docker approximation). The catch: it clones production wholesale, meaning live customer PII and real payment/API credentials come along by default, and the node gets a public URL. This plugin closes that gap — every node it creates is anonymized and sandboxed automatically, before it's ever reported ready, so "let the client's AI poke at a real prod clone" doesn't also mean "expose real customer data on the internet."
Setup
Three steps: install the plugin, tell it your Hypernode token, restart Claude Code.
1. Install the plugin
Type this directly into Claude Code (no terminal needed):
/plugin marketplace add ProxiBlue/pb-hypernode-mcp@latest
/plugin install pb-hypernode-mcp@pb-hypernode-mcp
Claude Code fetches everything straight from GitHub — no downloading, no separate server to run, nothing to clone by hand.
@latest pins you to the newest tested release rather than whatever's mid-development on main. To pin to a specific version instead (for reproducibility, e.g. across a team), use its tag directly — check Releases for the current version number, then:
/plugin marketplace add ProxiBlue/pb-hypernode-mcp@vX.Y.Z
If Claude Code reports "already at the latest version" but you know a newer release exists, it's comparing version numbers, not git commits — re-running marketplace update does nothing if the version string didn't change between releases. Remove and re-add the marketplace to force a fresh fetch:
/plugin marketplace remove pb-hypernode-mcp
/plugin marketplace add ProxiBlue/pb-hypernode-mcp@latest
/plugin install pb-hypernode-mcp@pb-hypernode-mcp
/reload-plugins
(If you'd rather run it from a terminal instead, the same commands work as claude plugin marketplace add ... / claude plugin install ....)
2. Add your Hypernode API token(s)
Hypernode API tokens are scoped per Hypernode, not account-wide — there's no single token that works across every app you manage. This plugin needs one token per Hypernode you want it to touch, set as a JSON object mapping <appname> to its token. It never gets stored anywhere by the plugin — you set it as an environment variable, the same way you'd set any password-like value.
Find each app's token in its Hypernode Control Panel, then in your terminal (before opening Claude Code):
export HYPERNODE_API_TOKENS='{"myapp":"token1","myapp2":"token2"}'
Managing a single Hypernode? A one-entry map still works:
export HYPERNODE_API_TOKENS='{"myapp":"your-token-here"}'
The map's keys ARE the allowlist — only apps with an entry here can be operated on. An app with no token configured raises a clear error rather than silently trying (and failing) with the wrong credentials.
Tip: add this line to your shell's startup file (~/.zshrc or ~/.bashrc) so you don't have to re-type it every time.
3. Restart Claude Code
Close and reopen Claude Code so it picks up the token and connects to the plugin. You're ready to go.
Quick start
Just ask, in plain English:
"Spin up a Brancher preview for myapp so I can show the client the new category page layout."
Claude creates the node, waits for it to come online, sanitizes it (see Safety guardrails), and reports back:
node_name: myapp-eph482913
access_url: https://myapp-eph482913.hypernode.io/
minutes_remaining: 387
From there, ask it to make a change and show you the result, or just say "clean up any leftover preview nodes" when you're done — Brancher bills by the minute whether or not anyone's looking at it.
What's in the plugin
skills/
├── brancher-spinup/ create a sanitized preview node, report access details
├── brancher-preview/ full loop: spin up -> change -> build -> screenshot
└── brancher-cleanup/ list/flag/delete leftover nodes
src/pb_hypernode_mcp/ the MCP server (7 tools) — see MCP tools below
tests/ automated test suite
Requirements
- A Hypernode account on a Falcons plan, with an API token from the Control Panel (Brancher is a Falcons-only feature).
- The SSH key you already use to reach your Hypernode — nothing extra to set up, Brancher preview nodes inherit access automatically.
- Python 3.11+ and
uvinstalled on the machine running Claude Code (Claude Code plugins are just code — this is the runtime they need).
MCP tools
All 7 tools are registered on the pb-hypernode-mcp server (src/pb_hypernode_mcp/server.py). brancher_exec and brancher_put shell out to the system ssh/rsync binaries using your already-configured local SSH agent/key — this plugin never holds or stores key material itself. Every tool that calls the Hypernode REST API resolves its token per-app from HYPERNODE_API_TOKENS; an appname with no entry in that map has nothing to authenticate with and the call fails with a clear error listing which apps ARE configured.
| Tool | Purpose | Key arguments |
|---|---|---|
brancher_create |
The sole node-creation tool: enforces a mandatory label and Falcons-plan eligibility, then wraps create -> wait-until-SSH-reachable -> run mandatory sanitization -> report ready as one non-bypassable call. There is no separate "raw create" tool — it is structurally impossible to create a Brancher node through this plugin without sanitization running first. Never returns an access_url for a node that has not finished sanitizing. Raises NodeUnreachableTimeoutError if the node never becomes SSH-reachable within 300s, or SanitizationFailedError (access URL withheld) if a sanitization command fails partway through. |
appname (str), labels (list[str], required, at least one), clear_services (list[str], optional, defaults to ["cron"]) |
brancher_list |
List active Brancher nodes for appname. Returns each node's name, host, and minutes (wall-clock uptime since creation, not idle-aware). Rejects any appname with no configured token. |
appname (str) |
brancher_delete |
Delete a Brancher node. Gated behind a confirm=True re-call: the first call (default confirm=False) looks up and returns the target node's details plus a confirmation prompt without deleting anything; only a second call with confirm=True issues the actual DELETE. Validates the node name against the -eph<id> pattern first. |
node_name (str, <appname>-eph<id>), confirm (bool, default False) |
brancher_ssh_info |
Return SSH connection details (host, user, port) for a node, without opening a connection itself. Raises NodeNotReadyError if the node has no IP assigned yet. |
node_name (str) |
brancher_exec |
Run a shell command on a Brancher node over SSH (shells out to the system ssh binary). The single safety-critical chokepoint of the "change-it" layer: refuses any node_name that doesn't match the -eph<id> pattern, before spawning any subprocess — structurally impossible to point this tool at a production host. Returns stdout/stderr/exit_code; raises SshConnectionError on ssh's own exit code 255, SshCommandTimeoutError on timeout. |
node_name (str), command (str), timeout (float, default 30s) |
brancher_put |
Sync a local file/directory to a Brancher node via rsync -az --protect-args over SSH. Same -eph-only guard and local-SSH-agent connection model as brancher_exec. Raises SyncError on non-zero rsync exit. |
node_name (str), local_path (str), remote_path (str), port (int, default 22) |
brancher_apps |
List every Hypernode <appname> with a configured API token — pure local config introspection, no REST API call. Skills call this first whenever a request doesn't say which Hypernode/app to target, so they never have to guess. |
none |
Skills
brancher-spinup— spin up a disposable Brancher preview node cloned from production, with mandatory automatic sanitization, and report its access URL. Use when a client asks to preview a change on a real prod-clone environment before it ships. Wraps the singlebrancher_createtool call — never reproduces the create/wait/sanitize sequence by hand.brancher-preview— the full loop: spin up a node (via thebrancher-spinupskill), apply a code change (push a local diff withbrancher_put, or edit in place withbrancher_exec), run only the Magento build commands the change actually needs (decide_build_commands()insrc/pb_hypernode_mcp/preview_logic.py), view the result through whatever browser MCP tool is already in the session, then explicitly remind the user the node is still billing Brancher minutes. Use when a client wants an end-to-end look at a change on a disposable environment. Never deletes the node itself.brancher-cleanup— list active nodes withbrancher_list, flag any at or past an age threshold (minutes >= threshold_minutes, default 240 minutes / 4 hours, viaflag_stale_nodes()insrc/pb_hypernode_mcp/cleanup_logic.py), and delete flagged nodes (single or bulk) only after explicit user confirmation. Use when a client wants to check for or remove leftover Brancher nodes to stop minute accrual. Brancher bills wall-clock minutes from creation regardless of whether anyone is actively using the node.
Multiple Hypernodes
Every Hypernode needs its own entry in HYPERNODE_API_TOKENS — there is no account-wide token. All three skills call brancher_apps first whenever a request doesn't say which app to target, show the configured list, and ask before doing anything else; they never guess an appname. If only one app is configured, they proceed with it and just say so, rather than asking a pointless confirmation question.
Safety guardrails
-
Mandatory sanitization — cannot be disabled. Every
brancher_createcall runs the full sanitization sequence (src/pb_hypernode_mcp/sanitization/) against the node before it is ever reported"ready"or returns anaccess_url. There is no flag, config option, or bypass path —brancher_createis the ONLY node-creation MCP tool this plugin registers (there is no separate, unsanitized create tool), andspinup_sanitized_brancher_node()insrc/pb_hypernode_mcp/tools/brancher_spinup_flow.py(the function behind it) structurally cannot return an access URL without every sanitization command having exited 0 first. If a sanitization command fails partway through, the tool raisesSanitizationFailedErrorand deliberately withholds the access URL — the exception does not even carry it, so a catching caller has no way to accidentally surface it.The sequence (config-driven, Magento-shaped default in
sanitization/config.py::DEFAULT_MAGENTO_SANITIZATION_CONFIG):- PII anonymization —
UPDATEstatements (vian98-magerun2 db:query) againstcustomer_entity,customer_address_entity,sales_order,sales_order_address(names/emails/phones/street replaced with anonymized placeholders), and stored card data (quote_payment,sales_order_payment:cc_number_enc,cc_cid_enc,cc_owner,additional_datanulled). - Admin credential reset —
admin_userusername/email reset to placeholder values and password overwritten with a hash that is deliberately invalid for any real password (locks form-based login until an operator sets a real one viabin/magento admin:user:create). - Payment gateway sandbox-forcing —
bin/magento config:setforces e.g.payment/braintree/environment=sandbox,paypal/general/sandbox_flag=1. - Third-party API key stubbing —
bin/magento config:setreplaces live keys (e.g. ShipperHQ, AvaTax) with dummy sandbox values so no preview node can make a real charge or a real third-party API call under production credentials.
A real client app's exact table shape and installed integrations should override/extend
SanitizationConfig, not rely on the shipped default in production — it exists as a safe-by-default starting point, not a promise it matches every schema. - PII anonymization —
-
Per-app token allowlist (
HYPERNODE_API_TOKENS) — the map's keys ARE the allowlist.brancher_create,brancher_list,brancher_delete, andbrancher_ssh_inforefuse anyappnamewith no configured token — there is no separate allowlist mechanism to fall out of sync with the token map. -
Falcons-plan eligibility check —
brancher_createrejects apps not on a Brancher-eligible plan before creating anything. -
-eph-only guard —brancher_execandbrancher_putvalidatenode_nameagainst the<appname>-eph<id>pattern (tools/_guards.py::validate_eph_node_name,.fullmatch()— no partial-match or trailing-character gaps) before opening any SSH connection or subprocess. It is structurally impossible to point either tool at a production hostname. -
Confirm-before-delete —
brancher_deletenever deletes on the first call. It requires an explicitconfirm=Truere-call after showing the target node's details; a threshold being configured or a node being flagged as stale is never itself confirmation. -
Mandatory label —
brancher_createrejects calls with nolabels, so every node is traceable to a reason/ticket. -
Token handling —
HYPERNODE_API_TOKENSis read from the environment only and is never written to disk or plugin config by this plugin. Each Hypernode's token is only ever used to authenticate requests for its own app. -
brancher_putargument hardening —remote_path/local_pathare shell-quoted and rsync runs with--protect-args, so the remote host's shell never re-parses a path argument, closing off metacharacter injection via a crafted path.
This design was checked by a 3-specialist security review before release (static analysis, adversarial testing, defensive audit). It caught a real critical gap in an earlier draft — the sanitized flow had been built as a second tool alongside a still-exposed raw, unsanitized create path — which is why "one creation tool, no exceptions" is called out so insistently above. Found a security issue? Open an issue rather than a PR with the exploit details.
Limitations (v1)
- Magento/Mage-OS only. The sanitization layer's default config (
DEFAULT_MAGENTO_SANITIZATION_CONFIG) and thebrancher-previewskill's build-command decision logic (decide_build_commands()) are both Magento-shaped. This is not a generic multi-platform tool — WooCommerce, Shopware, Laravel, and other Hypernode-hosted platforms are out of scope for v1. A non-Magento app would need a hand-writtenSanitizationConfigat minimum, and the preview skill's build sequence would not apply. - No MCP-managed SSH keys.
brancher_exec/brancher_putshell out to the systemssh/rsyncbinaries and rely entirely on your own local SSH agent/key already having access to Brancher nodes (which inherit access automatically via Brancher's full-filesystem clone from production). This plugin never provisions, stores, or transmits key material. - stdio transport only. No remote/HTTP MCP transport in v1 — this is a local Claude Code plugin, run per-developer against their own
HYPERNODE_API_TOKENS. There is no hosted/managed version of this MCP. Token and SSH access are both entirely client-owned. - REST API only. No Hypernode Deploy (
deploy.php) integration in v1. - Wall-clock, not idle-aware, minute accounting.
brancher-cleanup's staleness check usesminutesas reported by the Hypernode API (uptime since creation) — it cannot tell an idle node from an actively used one. - Unverified API response shapes.
brancher_list's expected response shape ({"nodes": [{"name", "host", "minutes"}, ...]}) andbrancher_create's plan/minutes field names (plan_type,brancher_minutes_remaining) are documented assumptions, not yet confirmed against the live Hypernode API contract — see the module docstrings insrc/pb_hypernode_mcp/tools/brancher_list.pyandsrc/pb_hypernode_mcp/tools/brancher_create.pyif API responses don't match at runtime. Run a real create ->brancher_exec whoamismoke test against a Falcons-plan account before pointing this at a client. - Playwright test offloading not yet built. Running the functional test suite against a Brancher node instead of local/CI is tracked separately — see ProxiBlue/pb-hypernode-mcp#1 or the originating design ticket.
Development
git clone https://github.com/ProxiBlue/pb-hypernode-mcp
cd pb-hypernode-mcp
uv sync --extra dev
uv run pytest -v # mocked HTTP/SSH — no real Hypernode account touched
uv run ruff check src tests # lint
uv run ruff format --check src tests # format check
uv run pyright src tests # type check
No integration tests run automatically against a real Hypernode account. If you're changing tools/brancher_exec.py or the reachability-polling logic in tools/brancher_spinup_flow.py, do a manual smoke test against a real Falcons-plan node before merging — mocks can't catch a wrong SSH-user assumption or a shape mismatch in the real API response.
To install your own clone for local development instead of the published version, point Claude Code at the folder directly:
claude plugin marketplace add pb-hypernode-mcp /path/to/your/clone
claude plugin install pb-hypernode-mcp@pb-hypernode-mcp
After editing skills or server code, run claude plugin update pb-hypernode-mcp@pb-hypernode-mcp to pick up the change without re-adding the marketplace.
If the plugin doesn't show up after installing, check: claude plugin list shows pb-hypernode-mcp as enabled; a fresh Claude Code session lists the brancher_* tools and the three brancher-* skills; HYPERNODE_API_TOKENS is set in the same shell you launched Claude Code from.
Cutting a release
Bump version in both pyproject.toml and .claude-plugin/plugin.json — Claude Code's /plugin marketplace update compares plugin.json's version string, not the git commit. Moving latest without bumping this means installed users get told "already at the latest version" even though the underlying code changed. Then:
git tag vX.Y.Z
git push origin vX.Y.Z
git tag -f latest # move the floating tag to this commit
git push origin latest --force
vX.Y.Z tags are permanent and never move. latest always points at the newest one — that's the only tag ever force-pushed.
License
Apache-2.0. See LICENSE and NOTICE for third-party dependency/service attribution (Hypernode Brancher API, system ssh/rsync, MCP Python SDK).
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。