v5.0.0: M365/Entra OAuth connector mode + GHCR image#7
Merged
Conversation
Adds an HTTP auth mode that lets bookstack-mcp be added as a remote Claude Connector gated by Microsoft 365 / Entra ID login, with no API key or client credential entry by the user. Modeled on zereight/gitlab-mcp's OAuth proxy. - New src/oauth/entra-proxy.ts: self-contained, BookStack-agnostic OAuth proxy. Hosts RFC 9728 protected-resource metadata, RFC 8414 AS metadata, local DCR (RFC 7591), and /authorize /callback /token, brokering login upstream to Entra. Stateless per-request bearer validation via jose remote JWKS (issuer, audience, expiry). Short-lived in-memory stores for DCR clients, pending auth, and codes. - Two shared service tokens: read-only (BOOKSTACK_TOKEN_*) and write-capable (BOOKSTACK_WRITE_TOKEN_*). Write is gated per session by an Entra app role (OAUTH_WRITE_ROLE); non-writers' sessions are bound to the read-only token, so a gating bug still cannot write. - index.ts: startHttp mounts the OAuth surface when MCP_OAUTH_ENABLE=true, enforces the bearer on /mcp (401 with WWW-Authenticate bootstraps discovery), and selects the per-session credential/tool set by role. stdio and no-auth HTTP modes are unchanged and backward compatible. - New env: MCP_OAUTH_ENABLE, MCP_SERVER_URL, MCP_TRUST_PROXY, OAUTH_TENANT_ID, OAUTH_CLIENT_ID/SECRET, OAUTH_AUDIENCE, OAUTH_SCOPES, OAUTH_WRITE_ROLE, BOOKSTACK_WRITE_TOKEN_ID/SECRET. - Add jose as a direct dependency; design spec under docs/superpowers/specs.
Ships a container image and the deployment/setup documentation needed to test the M365/Entra OAuth connector on ceres. - Dockerfile (node:20-alpine, multi-stage, non-root, dumb-init, HEALTHCHECK on /health) binding 0.0.0.0:8080; npm ci --ignore-scripts so the prepare hook doesn't run the build before src/ is copied. Add .dockerignore. - ci.yml: docker-preview job builds and pushes ghcr.io/ttpears/bookstack-mcp: branch-<slug> for every PR (GITHUB_TOKEN, amd64) so a branch can be pulled and deployed on ceres before it ships — mirrors the gitlab-mcp-custom pattern. - release.yml: docker job builds and pushes :X.Y.Z, :X.Y, :latest (amd64+arm64) on version tags. - docs/SETUP.md: full runbook — step-by-step Entra app registration (single tenant, expose-api api://<client-id>, Writer app role, v2 tokens, assignment required + role assignment), env var reference, two-token BookStack setup, internal LibreChat deploy on ceres, and the public OAuth connector deploy. - README: link to the runbook and note the GHCR image + preview tags. Verified: docker build + run green; /health 200, PRM 200, unauthenticated /mcp 401, container HEALTHCHECK healthy. Workflow YAML parses.
ttpears
force-pushed
the
feat/m365-oauth-connector
branch
from
June 28, 2026 04:15
145f018 to
35c6ae8
Compare
…ence (#8) Move the entra-proxy's in-memory clients/pending/codes maps behind an async KvStore (in-memory default, Redis when REDIS_URL is set), so DCR clients and in-flight auth flows survive redeploys and can be shared across replicas. Keys namespaced by issuer host. Mirrors ttpears/gitlab-mcp. Co-authored-by: Tim Pearson <ttpearso@gleim.com>
Adds four content-oriented MCP prompts surfaced as one-click actions in Claude's connector menu (prompts capability), driving the existing read tools: - answer_from_wiki (question) — search + read top pages + cited answer - find_pages (topic) — ranked search results with previews + links - summarize_book (book name/id) — structured book summary with section links - recent_changes (days, default 30) — grouped summary of recent edits New src/prompts.ts registers them via server.registerPrompt (auto-advertises the capability + prompts/list,get); wired into buildServer. Registered in all modes (read-only, no per-user identity). Verified via local prompts/list + prompts/get.
plugin.json was stranded at 3.5.0 and package-lock.json at 5.0.0 while package.json moved to 5.1.0. Sync both so the marketplace and lockfile match the release. The plugin manifest version is what Claude Code's plugin cache keys on. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lets bookstack-mcp run as a remote Claude Connector gated by Microsoft 365 / Entra ID login — users sign in, no API key or client credential to paste. Modeled on zereight/gitlab-mcp's OAuth proxy and our
gitlab-mcp-customdeployment pattern.OAuth (v5.0.0)
src/oauth/entra-proxy.ts: self-contained, BookStack-agnostic OAuth proxy. Hosts RFC 9728 protected-resource metadata, RFC 8414 AS metadata, local DCR (RFC 7591), and/authorize/callback/token, brokering login upstream to Entra. Stateless per-request bearer validation viajoseremote JWKS (issuer, audience, expiry).OAUTH_WRITE_ROLE, defaultWriter); non-writers are bound to the read-only token, so a gating bug still cannot write.index.tsmounts the OAuth surface whenMCP_OAUTH_ENABLE=true, enforces the bearer on/mcp(401 +WWW-Authenticatebootstraps discovery), and selects the per-session credential/tool set by role. stdio and no-auth HTTP are unchanged and backward compatible.Image + CI
Dockerfile(node:20-alpine, multi-stage, non-root, dumb-init, HEALTHCHECK on/health).ci.yml: per-PRdocker-previewjob →ghcr.io/ttpears/bookstack-mcp:branch-<slug>(this PR::branch-feat-m365-oauth-connector) for testing on ceres.release.yml: onv*tags →:X.Y.Z,:X.Y,:latest(amd64+arm64).Docs
docs/SETUP.md: step-by-step Entra app registration, env reference, two-token BookStack setup, internal LibreChat deploy on ceres, and the public OAuth connector deploy. Design spec underdocs/superpowers/specs/.Verification
type-check+buildclean;docker build/rungreen./register,/authorize→ Entra redirect (PKCE preserved), unauthenticated/mcp→ 401 withWWW-Authenticate, container HEALTHCHECK healthy, and no-auth mode still initializes a session.