Repository navigation
Releases
v0.1.0
byward v0.1.0
A sign-in gate for MCP servers. byward puts any MCP server, HTTP or stdio, behind OAuth 2.1 and your identity provider, with first-class Microsoft Entra ID support. One Go binary, configured by environment variables.
First release. byward is pre-1.0 and has not had an independent security audit. Read the security posture before deploying it.
What it does
- Sign-in for any MCP server. MCP clients connect to byward with standard OAuth 2.1; byward signs the user in with Microsoft Entra ID, shows its own consent page, and issues short-lived tokens of its own. The MCP server never sees a client's or the IdP's token.
- Clients connect without manual setup. Client ID Metadata Documents (CIMD) and Dynamic Client Registration (DCR), plus static clients for anything else.
- HTTP and stdio upstreams. Put a remote MCP server or a local command behind sign-in; stdio sessions are bound to the user who started them, with per-user and per-upstream process limits.
- Entra done properly. Single-tenant apps, v2 tokens, app roles and groups as an access gate, and
byward doctor/byward init entrato set up and check the app registration. - Revocation that works. Rotating refresh tokens with reuse detection,
/revoke, andbyward revoke-user/byward revoke-allfor operators. - Operable. SQLite state with restore detection, per-address rate limits on the OAuth endpoints, a structured audit stream, Prometheus metrics, and hardening checks at startup.
Verified clients
Signed in through byward against a real Entra tenant and called a tool:
| Client | How it registers |
|---|---|
| Claude Code | CIMD (or a static client) |
| claude.ai | CIMD |
| ChatGPT | CIMD |
| VS Code | CIMD |
| Cursor (3.29.9; 2.6.22 does not work) | DCR |
The TypeScript, Python and Go MCP SDK clients and MCP Inspector run against byward in CI on every change.
Install
docker pull ghcr.io/byward-dev/byward:v0.1.0Linux binaries (amd64, arm64) are attached below; macOS binaries are for development only. Verify a release with the commands in packaging (cosign keyless signature, SBOM and build provenance).
Start with the Entra quickstart, then the hardening checklist.
Since v0.1.0-rc.2
- Log redaction matches a JWT by its shape, so a random client ID that happens to contain
eyjis no longer logged as[REDACTED](#230). - The release itself: pre-releases no longer move
:latest, and the changelog leaves out author emails.
Not in this release
On-Behalf-Of tokens for downstream APIs, Entra app-only tokens, identity assertion to the upstream, and identity providers other than Entra are planned for v0.2.
MCP is a trademark of LF Projects, LLC. byward is an independent project.
Changelog since v0.1.0-rc.2
v0.1.0-rc.2
byward v0.1.0-rc.2
Second release candidate for v0.1.0. It is rc.1 plus two relay fixes, both found by tests after rc.1:
- The HTTP relay no longer forwards client request trailers, upstream protocol upgrades, response trailers, or 1xx headers other than
Link. An interim 101 is dropped, and byward's own response headers survive a 1xx. - An
initializewhose stdio process exits immediately gets 502, not 404.
Pre-release. byward is pre-1.0 and has not had an independent security audit. Read the security posture before deploying it.
docker pull ghcr.io/byward-dev/byward:v0.1.0-rc.2Verify the image and the archives with cosign, or with gh attestation verify, as described in packaging.
Changelog
v0.1.0-rc.1
byward v0.1.0-rc.1
Release candidate. Please try it and report problems in Issues; v0.1.0 follows once the candidate holds up.
A sign-in gate for MCP servers. byward puts any MCP server, HTTP or stdio, behind OAuth 2.1 and your identity provider, with first-class Microsoft Entra ID support. One Go binary, configured by environment variables.
First release. byward is pre-1.0 and has not had an independent security audit. Read the security posture before deploying it.
What it does
- Sign-in for any MCP server. MCP clients connect to byward with standard OAuth 2.1; byward signs the user in with Microsoft Entra ID, shows its own consent page, and issues short-lived tokens of its own. The MCP server never sees a client's or the IdP's token.
- Clients connect without manual setup. Client ID Metadata Documents (CIMD) and Dynamic Client Registration (DCR), plus static clients for anything else.
- HTTP and stdio upstreams. Put a remote MCP server or a local command behind sign-in; stdio sessions are bound to the user who started them, with per-user and per-upstream process limits.
- Entra done properly. Single-tenant apps, v2 tokens, app roles and groups as an access gate, and
byward doctor/byward init entrato set up and check the app registration. - Revocation that works. Rotating refresh tokens with reuse detection,
/revoke, andbyward revoke-user/byward revoke-allfor operators. - Operable. SQLite state with restore detection, per-address rate limits on the OAuth endpoints, a structured audit stream, Prometheus metrics, and hardening checks at startup.
Verified clients
Signed in through byward against a real Entra tenant and called a tool:
| Client | How it registers |
|---|---|
| Claude Code | CIMD (or a static client) |
| claude.ai | CIMD |
| ChatGPT | CIMD |
| VS Code | CIMD |
| Cursor (3.29.9; 2.6.22 does not work) | DCR |
The TypeScript, Python and Go MCP SDK clients and MCP Inspector run against byward in CI on every change.
Install
docker pull ghcr.io/byward-dev/byward:v0.1.0-rc.1Linux binaries (amd64, arm64) are attached below; macOS binaries are for development only. Verify a release with the commands in packaging (cosign keyless signature, SBOM and build provenance).
Start with the Entra quickstart, then the hardening checklist.
Not in this release
On-Behalf-Of tokens for downstream APIs, Entra app-only tokens, identity assertion to the upstream, and identity providers other than Entra are planned for v0.2.
MCP is a trademark of LF Projects, LLC. byward is an independent project.
Changelog
- edd03e4: feat: static clients are optional while CIMD or DCR is on (#138)
- 3fffc22: docs: Cursor and Claude Code sign-in as observed in the real run (#59) (#134)
- 8fa9c43: feat: remembered consent for static and trusted clients (#90) (#132)
- e0e0e03: test: real open-source MCP clients sign in through byward (slice 2.3) (#128)
- 163eb8c: fix: keep the IdP's new tokens when a refresh fails after renewing them (#92) (#130)
- 9e715bd: fix: the store refuses a counter file inside the state directory whose name starts with ".." (#123)
- 3be7365: test: Cursor × DCR client simulator profile (#59) (#129)
- fb4a6c9: feat: check --offline, doctor and init entra (slice 5.2) (#120)
- 4326649: test: client simulator profiles for the closed clients (#59) (#125)
- 2565112: feat: enforce MCP-Protocol-Version on stdio MCP sessions (#127)
- 091ce4c: feat: dynamic client registration (slice 2.2) (#124)
- e8654b3: test: restore drill on the integration lane; keys never reach the store (#119)
- aac26a9: feat: legacy-era stdio upstream (slice 3.1) (#122)
- 8e41a8a: docs: retire a sealing key 15 days after the last replica's restart (#104) (#117)
- 124ce86: fix: harden the upstream, the internal listener and secret files (#102, #103, #105) (#111)
- b9cc514: fix: run store collection in the background (#118)
- 80e8c74: feat: operator revoke-user and revoke-all commands (#101) (#116)
- 560d32e: feat: CIMD clients (slice 2.1) (#113)
- d093289: docs: move Entra app-only tokens to v0.2 (ADR-0022 amendment) (#115)
- e1f34e5: docs: move On-Behalf-Of to v0.2 (ADR-0017 amendment) (#114)
- c4e1d26: docs: scope ADR-0001's rate-limiting non-goal to MCP traffic (#112)
- eba633f: feat: bind legacy MCP sessions to the signed-in user (#100) (#109)
- 791a080: docs: record the trademark check in ADR-0028 (#110)
- 8353b73: test: bring the conformance lane up to the sweep rule (#97)
- ba751e6: docs: add the byward owl logo; README status is pre-release (#108)
- b5d23fa: docs: security posture and deployment hardening (#107)
- b039e7f: docs: Entra quickstart (slice 1.7) (#106)
- eba198d: chore: rename the product to byward (ADR-0028) (#99)
- 8842e1d: feat: SQLite store in the server; five-setting Entra quickstart (slice 1.3 wiring) (#98)
- e022245: fix: keep the login cookie under the browsers' 4 KB limit (#94) (#95)
- 3cf57f9: chore: flag stale test-lane pins weekly (#96)
- f5b7ac5: feat: rate-limit the OAuth endpoints per client address (#91)
- d85a483: feat: refresh with rotation, /revoke and a store-fed deny list (#87)
- f43dbbc: feat: consent page before the IdP sign-in (slice 1.4) (#83)
- 3181b88: docs: add the static clients file reference (#93)
- 34c7093: chore(deps): Bump docker/login-action from 4.6.0 to 4.7.0 (#78)
- 25740a1: chore: lint once per release GOOS in mise run check (#89)
- dc9767e: fix: refuse a non-ASCII host in the public URL, allowed origins and allowed hosts (#88)
- 1bc688d: feat: the access gate (slice 1.5) (#86)
- a78fd87: feat: audit stream, metrics, opt-in traces and health (slice 5.1) (#85)
- 8142d59: feat: add the SQLite store (#81)
- 1678915: fix: refetch JWKS once in the background and class key outages Unavailable (#84)
- 18fae04: feat: static clients sign in with Entra and call the upstream (slice 1.2) (#82)
- 670d8cf: test: add the MCP conformance lane (direct vs through the proxy) (#80)
- 1e5edfd: docs: add governance files (security, contributing, conduct, templates) (#69)
- b8052fc: feat: --insecure-no-auth, a loopback-only mode without authentication (ADR-0027) (#76)
- c15056d: feat: answer upstream refusals as JSON-RPC responses (ADR-0025) (#75)
- eab4f88: ci: add release pipeline with snapshot builds (#70)
- 64ef5c1: feat: add IdP adapters for generic OIDC and Entra (#73)
- 8270bbd: feat: add sealed proxy tokens and the sealing key ring (#74)
- bb73baa: feat: add the stdio upstream process core (#72)
- 1d0edf3: test: add fake IdP with entra and generic profiles (#71)
- 1145297: feat: relay to an HTTP upstream behind a 401 (#68)
- 2c5c260: docs: decide F21 testing strategy (#46)
- 648efcf: docs: decide F22 project governance (#44)
- 04e76cb: docs: decide F15 machine-to-machine (#43)
- a2535dd: docs: decide F20 observability (#42)
- 7d6313f: docs: decide F18 configuration (#41)
- 8697475: docs: decide F19 packaging (#40)
- dfd61ec: chore: move to Go 1.27.3 and golangci-lint 2.15.0 (#45)
- 9e75f82: docs: decide F13 identity propagation, F14 delegation and F17 hardening (#39)
- 576252a: docs: add ADR index (#38)
- 8f61090: docs: decide F11 token validation and F12 access gate (#37)
- e6493d4: docs: decide F16 discovery and errors (#36)
- 1f0d835: chore: pin azure-cli in mise for operator tasks, keep it out of CI (#35)
- 0a107dd: docs: d...