Per-plugin sidecar that exposes local MCP servers to a Zenyard backend over an outbound WebSocket.
Project description
zenyard-relay
zenyard-relay connects an MCP server running on a user's machine to the
Zenyard backend, even when that machine is behind a NAT or firewall. It
dials out to the backend so the server can be reached without exposing any
inbound ports.
Run one zenyard-relay serve process per MCP server you want to expose.
Each process is a transparent passthrough — it forwards traffic between the
backend and the MCP server unchanged. Multiple servers on the same machine
appear to the backend as a single device.
The MCP server can run as a local subprocess (stdio) or a remote HTTP
endpoint (streamable-http).
Install
The relay ships as a Python wheel built by maturin;
the wheel installs the native zenyard-relay binary into the environment's
bin/ (or Scripts\ on Windows):
pip install zenyard-relay
Or build from source with a stable Rust toolchain (≥ 1.75):
cargo build --release
The binary lands at target/release/zenyard-relay. Wheels for the common
platforms are published from .github/workflows/ci.yml (tests gate the
wheel publish step).
One-time device setup
serve runs without any config file as long as --control-endpoint (or
--api-url) and the ZENYARD_RELAY_TOKEN env var are supplied. For
convenience, a shared ~/.zenyard/relay.json can hold the control endpoint
(and optionally the token) so individual sidecars don't have to pass it on
the command line:
zenyard-relay config init # optional; writes a starter ~/.zenyard/relay.json
zenyard-relay config path # prints the config path
zenyard-relay config validate # validates the file
~/.zenyard/relay.json example:
{
"config": {
"control_endpoint": "wss://relay.zenyard.dev/relay/control",
"device_description": "ziv-laptop (Linux)"
}
}
Plugins that already have the backend HTTP API URL configured can pass it
as-is — the relay swaps http/https for ws/wss and appends the
fixed /relay/control path:
zenyard-relay serve --id <ID> --api-url http://localhost:30465/ ...
The same field is accepted in relay.json as "api_url" (mutually
exclusive with "control_endpoint").
The auth token is preferred from ZENYARD_RELAY_TOKEN (keeps secrets out
of the JSON file); config.token is the fallback.
Sidecar contract
zenyard-relay serve --id <UPSTREAM_ID> [--display-name <NAME>] [--description <TEXT>] [--tag K=V...]
[--control-endpoint <URL> | --api-url <URL>]
[--config <PATH>] [--log-level <LEVEL>] [--log-format <FMT>]
(--command <CMD> [--arg <V>...] [--env K=V...] [--cwd <DIR>])
| (--url <URL> [--header "Name: value"...])
--control-endpoint / --api-url may be omitted when ~/.zenyard/relay.json
(or the file pointed at by --config) supplies one. ZENYARD_RELAY_TOKEN
in the environment is preferred over config.token.
Example — stdio upstream:
zenyard-relay serve --id fs --display-name "Filesystem" \
--command npx --arg -y --arg @modelcontextprotocol/server-filesystem --arg /tmp
Example — remote upstream:
zenyard-relay serve --id api --display-name "Example API" \
--url https://api.example.com/mcp \
--header "Authorization: Bearer ghp_..."
After connecting, the sidecar sends a single Hello frame carrying the
connection identity (relay_id / upstream_id) and the initial metadata
snapshot (display_name / description / tags). It then reads stdin
line-by-line for update ops that mutate the announced metadata:
{"op":"update", "display_name":"Echo (renamed)"}
{"op":"update", "tags":{"file":"sample.exe"}}
{"op":"update", "description":"Decompilation tools for sample.exe"}
Each update op replaces the supplied fields and re-sends
UpstreamUpdated. The sidecar logs lifecycle events (connections, sessions,
auth failures) to stderr per --log-level / --log-format, and exits when
stdin closes.
For "same resource → same upstream across restarts" semantics, derive
--id deterministically from the resource (e.g.
ida-<sha256(idb_path)>). Conflicting live claims on the same id from
two sidecars trigger takeover at the backend: the newer connection wins,
the older one's sessions continue until they close naturally.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Ok |
| 1 | Generic error |
| 2 | Invalid configuration / fatal auth rejection |
| 3 | Superseded — another relay took over this (relay_id, upstream_id) |
License
This project is licensed under the GNU Affero General Public License v3.0 only.
See the LICENSE file in this repository for the full license text.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file zenyard_relay-0.1.0.tar.gz.
File metadata
- Download URL: zenyard_relay-0.1.0.tar.gz
- Upload date:
- Size: 94.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e34c7bd44016d570295391e7ba685e3ba09e97004adf59642bd98408b6bb9427
|
|
| MD5 |
c9eebf96acc3769c008111b8d4d80538
|
|
| BLAKE2b-256 |
819396f5d69d87227b2f073f8f2f2734f66d7f1794084a8c802083f201b4ff35
|
Provenance
The following attestation bundles were made for zenyard_relay-0.1.0.tar.gz:
Publisher:
ci.yml on zenyard/zenyard-relay
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zenyard_relay-0.1.0.tar.gz -
Subject digest:
e34c7bd44016d570295391e7ba685e3ba09e97004adf59642bd98408b6bb9427 - Sigstore transparency entry: 1757122501
- Sigstore integration time:
-
Permalink:
zenyard/zenyard-relay@9795736cb8d2430980cdd0c7c0b572744247f859 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/zenyard
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@9795736cb8d2430980cdd0c7c0b572744247f859 -
Trigger Event:
push
-
Statement type:
File details
Details for the file zenyard_relay-0.1.0-py3-none-win_amd64.whl.
File metadata
- Download URL: zenyard_relay-0.1.0-py3-none-win_amd64.whl
- Upload date:
- Size: 4.7 MB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
82dcb4c9148b75a854c62763b1e71d085cfd6bcef6ce62bf55562643f984078e
|
|
| MD5 |
12bf579569ce82d9be98e565052608c4
|
|
| BLAKE2b-256 |
3db83bd0accc1c0fb176ecbe22bf0108eb6f63c063b2c286ad09d4c0d573363b
|
Provenance
The following attestation bundles were made for zenyard_relay-0.1.0-py3-none-win_amd64.whl:
Publisher:
ci.yml on zenyard/zenyard-relay
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zenyard_relay-0.1.0-py3-none-win_amd64.whl -
Subject digest:
82dcb4c9148b75a854c62763b1e71d085cfd6bcef6ce62bf55562643f984078e - Sigstore transparency entry: 1757122522
- Sigstore integration time:
-
Permalink:
zenyard/zenyard-relay@9795736cb8d2430980cdd0c7c0b572744247f859 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/zenyard
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@9795736cb8d2430980cdd0c7c0b572744247f859 -
Trigger Event:
push
-
Statement type:
File details
Details for the file zenyard_relay-0.1.0-py3-none-musllinux_1_2_x86_64.whl.
File metadata
- Download URL: zenyard_relay-0.1.0-py3-none-musllinux_1_2_x86_64.whl
- Upload date:
- Size: 5.0 MB
- Tags: Python 3, musllinux: musl 1.2+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ed9c86d1a39a1bc129c7c1094945c74ce42b5bf8ad77f78dd7650f2184d68e93
|
|
| MD5 |
f182f3116b8304197ec8bb249c535744
|
|
| BLAKE2b-256 |
1068e332c6d181d96ccccb5a232783eb66cc2cd0ead63e4601b584f97e1dcf4c
|
Provenance
The following attestation bundles were made for zenyard_relay-0.1.0-py3-none-musllinux_1_2_x86_64.whl:
Publisher:
ci.yml on zenyard/zenyard-relay
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zenyard_relay-0.1.0-py3-none-musllinux_1_2_x86_64.whl -
Subject digest:
ed9c86d1a39a1bc129c7c1094945c74ce42b5bf8ad77f78dd7650f2184d68e93 - Sigstore transparency entry: 1757122610
- Sigstore integration time:
-
Permalink:
zenyard/zenyard-relay@9795736cb8d2430980cdd0c7c0b572744247f859 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/zenyard
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@9795736cb8d2430980cdd0c7c0b572744247f859 -
Trigger Event:
push
-
Statement type:
File details
Details for the file zenyard_relay-0.1.0-py3-none-musllinux_1_2_aarch64.whl.
File metadata
- Download URL: zenyard_relay-0.1.0-py3-none-musllinux_1_2_aarch64.whl
- Upload date:
- Size: 4.6 MB
- Tags: Python 3, musllinux: musl 1.2+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
505b1c0372665909e7490144325356f712173da5ccafe5389d7b618d40c317bd
|
|
| MD5 |
5d23ae3b39ef63f882b68b9e39fd0d49
|
|
| BLAKE2b-256 |
70dcd84f0089be537daf528042a8683f8a11d34127882cd3f47923887ae80d61
|
Provenance
The following attestation bundles were made for zenyard_relay-0.1.0-py3-none-musllinux_1_2_aarch64.whl:
Publisher:
ci.yml on zenyard/zenyard-relay
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zenyard_relay-0.1.0-py3-none-musllinux_1_2_aarch64.whl -
Subject digest:
505b1c0372665909e7490144325356f712173da5ccafe5389d7b618d40c317bd - Sigstore transparency entry: 1757122542
- Sigstore integration time:
-
Permalink:
zenyard/zenyard-relay@9795736cb8d2430980cdd0c7c0b572744247f859 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/zenyard
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@9795736cb8d2430980cdd0c7c0b572744247f859 -
Trigger Event:
push
-
Statement type:
File details
Details for the file zenyard_relay-0.1.0-py3-none-manylinux_2_28_aarch64.whl.
File metadata
- Download URL: zenyard_relay-0.1.0-py3-none-manylinux_2_28_aarch64.whl
- Upload date:
- Size: 4.5 MB
- Tags: Python 3, manylinux: glibc 2.28+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
04627de4b3a2ceda48af0b12bde0e242abf7fefae0e68a7f13a3f0df30954b56
|
|
| MD5 |
1e424f811670d2f685bb8432232ba2e1
|
|
| BLAKE2b-256 |
086eb7bbe9f343887203b0d57d9fd835cf610a9303ac5d09daf779c5aa2a87f4
|
Provenance
The following attestation bundles were made for zenyard_relay-0.1.0-py3-none-manylinux_2_28_aarch64.whl:
Publisher:
ci.yml on zenyard/zenyard-relay
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zenyard_relay-0.1.0-py3-none-manylinux_2_28_aarch64.whl -
Subject digest:
04627de4b3a2ceda48af0b12bde0e242abf7fefae0e68a7f13a3f0df30954b56 - Sigstore transparency entry: 1757122560
- Sigstore integration time:
-
Permalink:
zenyard/zenyard-relay@9795736cb8d2430980cdd0c7c0b572744247f859 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/zenyard
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@9795736cb8d2430980cdd0c7c0b572744247f859 -
Trigger Event:
push
-
Statement type:
File details
Details for the file zenyard_relay-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: zenyard_relay-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 4.8 MB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fa1ccef4e823cc31059e7a487d93508fd3506165734a665a4b286c801be80b05
|
|
| MD5 |
1b0c07ec5bf9be23737d48053e444509
|
|
| BLAKE2b-256 |
8199da1b321a9a5f3bc774b456e6ec7c4d26acb84a4d8edc4987c8b91a44b1d1
|
Provenance
The following attestation bundles were made for zenyard_relay-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
ci.yml on zenyard/zenyard-relay
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zenyard_relay-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
fa1ccef4e823cc31059e7a487d93508fd3506165734a665a4b286c801be80b05 - Sigstore transparency entry: 1757122582
- Sigstore integration time:
-
Permalink:
zenyard/zenyard-relay@9795736cb8d2430980cdd0c7c0b572744247f859 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/zenyard
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@9795736cb8d2430980cdd0c7c0b572744247f859 -
Trigger Event:
push
-
Statement type:
File details
Details for the file zenyard_relay-0.1.0-py3-none-macosx_11_0_arm64.whl.
File metadata
- Download URL: zenyard_relay-0.1.0-py3-none-macosx_11_0_arm64.whl
- Upload date:
- Size: 4.6 MB
- Tags: Python 3, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
43e72457dd0511e2da3c4892583f42d204bcb35f036a48daa6cb7b5ae148b5eb
|
|
| MD5 |
7cf7b25c5934f0a3fd8927d52ea1b591
|
|
| BLAKE2b-256 |
419066e8912e847e15030abfdd1326df7e4912e85257d517121cb50409132a42
|
Provenance
The following attestation bundles were made for zenyard_relay-0.1.0-py3-none-macosx_11_0_arm64.whl:
Publisher:
ci.yml on zenyard/zenyard-relay
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zenyard_relay-0.1.0-py3-none-macosx_11_0_arm64.whl -
Subject digest:
43e72457dd0511e2da3c4892583f42d204bcb35f036a48daa6cb7b5ae148b5eb - Sigstore transparency entry: 1757122665
- Sigstore integration time:
-
Permalink:
zenyard/zenyard-relay@9795736cb8d2430980cdd0c7c0b572744247f859 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/zenyard
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@9795736cb8d2430980cdd0c7c0b572744247f859 -
Trigger Event:
push
-
Statement type:
File details
Details for the file zenyard_relay-0.1.0-py3-none-macosx_10_12_x86_64.whl.
File metadata
- Download URL: zenyard_relay-0.1.0-py3-none-macosx_10_12_x86_64.whl
- Upload date:
- Size: 4.8 MB
- Tags: Python 3, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5678c51e52eae201f4d9a9abe0b6ad4edf46829f61e7210f601153010d3306ba
|
|
| MD5 |
ed1f78a5e6a83b112e3a7091d2967f8a
|
|
| BLAKE2b-256 |
76ed384a7ce631176e4cb7085532dd53a7384fa902abae98731b269d8d608c92
|
Provenance
The following attestation bundles were made for zenyard_relay-0.1.0-py3-none-macosx_10_12_x86_64.whl:
Publisher:
ci.yml on zenyard/zenyard-relay
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zenyard_relay-0.1.0-py3-none-macosx_10_12_x86_64.whl -
Subject digest:
5678c51e52eae201f4d9a9abe0b6ad4edf46829f61e7210f601153010d3306ba - Sigstore transparency entry: 1757122641
- Sigstore integration time:
-
Permalink:
zenyard/zenyard-relay@9795736cb8d2430980cdd0c7c0b572744247f859 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/zenyard
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@9795736cb8d2430980cdd0c7c0b572744247f859 -
Trigger Event:
push
-
Statement type: