From 317cd05b71d5cedef96ec0f7d4b4a01f8318a663 Mon Sep 17 00:00:00 2001 From: Johnny Bouder Date: Thu, 22 Oct 2026 08:54:05 -0400 Subject: [PATCH] docs: catch up with MCP OAuth and group-only access - README: apps-mcp signs in with standard MCP OAuth, not the device flow. - MCP tools table: launch_app restricts by groups only (full Keycloak paths); the users argument is gone. - Upgrading: add the missing note for MCP OAuth (create the sign-in client and set mcp.oauth.clientId, reconnect agents, drop MCP_SESSION_TTL_SECONDS). - Local development: the images step also builds apps-authz:dev. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Ap1Tqx1QLHRGVVSXdxeduM --- README.md | 2 +- docs/src/content/docs/local-development.md | 4 ++-- docs/src/content/docs/mcp.md | 2 +- docs/src/content/docs/upgrading.md | 19 +++++++++++++++++++ 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e5814e3..8a22537 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ start again on the next request | apps-operator | Reconciles `App` resources into a Deployment, Service, ConfigMap and `NebariApp`, reports status, scales idle Apps to zero and serves the page they show while they start. | `operator` | | apps-api | REST API for CRUD, status, logs, events, analytics and zip/`.html` upload; writes `App` resources (and each App's write-only Secret) for the UI and the MCP server. | `api` | | apps-ui | The apps list as the home page (with a Needs attention banner, a Mine filter and Open links), app detail with logs and events, the launch form, and a Settings page (the UI and API versions, your groups, the cluster's settings, and how to connect an agent). | `ui` | -| apps-mcp | MCP server at `/mcp` that lets coding agents launch and manage apps (tools), attach an app's spec, status, logs and events as context (resources), and follow the triage, launch and access-review workflows (prompts), with Keycloak device-flow login. | `mcp` | +| apps-mcp | MCP server at `/mcp` that lets coding agents launch and manage apps (tools), attach an app's spec, status, logs and events as context (resources), and follow the triage, launch and access-review workflows (prompts), with standard MCP OAuth sign-in through Keycloak. | `mcp` | | apps-authz | Sidecar in the pods of Apps that can idle (records when the app last served a request) and of image Apps (gives them the one front port). It checks no tokens: `access.groups` are enforced at the gateway. | `authz` | Images are published to `ghcr.io/nebari-dev/apps-pack/` and diff --git a/docs/src/content/docs/local-development.md b/docs/src/content/docs/local-development.md index f4f7ba1..df3fa26 100644 --- a/docs/src/content/docs/local-development.md +++ b/docs/src/content/docs/local-development.md @@ -38,8 +38,8 @@ open http://docs-site.apps.nebari.test # the example app install Envoy Gateway, cert-manager, Keycloak (realm `nebari`, login `admin` / `nebari-admin`), and the nebari-operator itself. Pins the gateway Service to fixed NodePorts and creates the `apps` namespace labeled `nebari.dev/managed=true`. -3. **`images`** — builds `apps-operator:dev`, `apps-api:dev`, `apps-ui:dev`, and - `apps-mcp:dev` and loads them into the kind cluster. +3. **`images`** — builds `apps-operator:dev`, `apps-api:dev`, `apps-ui:dev`, + `apps-mcp:dev` and `apps-authz:dev` and loads them into the kind cluster. 4. **`deploy`** — installs the chart with `clusterDomain=nebari.test` and **`tls.enabled=false`** (plain HTTP — no certificate warnings locally). 5. Applies the inline example App and waits for `Running`. diff --git a/docs/src/content/docs/mcp.md b/docs/src/content/docs/mcp.md index 9936c60..2f853cb 100644 --- a/docs/src/content/docs/mcp.md +++ b/docs/src/content/docs/mcp.md @@ -117,7 +117,7 @@ the CA is trusted through [`mcp.orgCABundle`](/configuration/#apps-mcp). | Tool | Purpose | |---|---| | `describe_cluster` | Source types, launchable namespaces, apps domain, the cluster's default idle timeout and, when `image` is a source type, the `imageRegistries` images may come from — call first to pick valid options. | -| `launch_app` | Create + launch (static, Python/pixi via `pixi_task`, or a prebuilt container image with `source_type: image`, `image_reference` (tag or digest, from an allowed registry) and `image_port`, plus optional `image_command`, `image_args` and `image_run_as_user`; see [Container images](/launching-apps/#container-images)); optional `resources` (requests/limits, cluster defaults otherwise) and `groups`/`users` to restrict a private app (a member of any group or a listed user gets in; everyone else gets `403`); `idle_timeout_minutes` and `keep_alive` control [scale to zero](/app-crd-reference/#scale-to-zero); `manager_users`/`manager_groups` set who else may [manage](/launching-apps/#managers) it (omit to keep the current list); `secrets` (`{NAME: value}`) stores credentials in the app's own Secret instead of `env` (see [Secrets](#secrets)). **Idempotent on (namespace, name)** — re-launching an app you own or manage updates it instead of failing, so retries are safe; any other app is refused, never overwritten. Only the owner or an admin may change the managers. | +| `launch_app` | Create + launch (static, Python/pixi via `pixi_task`, or a prebuilt container image with `source_type: image`, `image_reference` (tag or digest, from an allowed registry) and `image_port`, plus optional `image_command`, `image_args` and `image_run_as_user`; see [Container images](/launching-apps/#container-images)); optional `resources` (requests/limits, cluster defaults otherwise) and `groups` to restrict a private app (full Keycloak paths matched exactly, `finance` is `/finance`; a member of any listed group gets in, every other signed-in user gets `403` at the gateway); `idle_timeout_minutes` and `keep_alive` control [scale to zero](/app-crd-reference/#scale-to-zero); `manager_users`/`manager_groups` set who else may [manage](/launching-apps/#managers) it (omit to keep the current list); `secrets` (`{NAME: value}`) stores credentials in the app's own Secret instead of `env` (see [Secrets](#secrets)). **Idempotent on (namespace, name)** — re-launching an app you own or manage updates it instead of failing, so retries are safe; any other app is refused, never overwritten. Only the owner or an admin may change the managers. | | `list_apps` | Apps with phase, URL, owner; optional namespace filter. Inline sources list file `paths` only. | | `get_app` | Full spec + status for one app; inline files come back fenced (see below). | | `get_app_status` | Lightweight poll: phase, URL, replicas, conditions. | diff --git a/docs/src/content/docs/upgrading.md b/docs/src/content/docs/upgrading.md index fbd969f..6a9dbf9 100644 --- a/docs/src/content/docs/upgrading.md +++ b/docs/src/content/docs/upgrading.md @@ -150,6 +150,25 @@ configures only apps-api and apps-mcp. next to an older apps-operator, so a restricted App never runs open; upgrade the chart and images together. +### Upgrading to MCP OAuth + +**Breaking:** MCP clients sign in themselves with standard +[MCP authorization](/mcp/#authentication): apps-mcp is an OAuth 2.0 protected resource with +the Keycloak realm as its authorization server, and it verifies the bearer token on every +request. The `authenticate` tool, the per-session device-flow token cache and +`MCP_SESSION_TTL_SECONDS` are removed. + +- **Create the sign-in client before you upgrade.** On a cluster with auth on, MCP clients + need a public Keycloak client with a loopback redirect URI, which the pack cannot + provision. A cluster admin creates it once ([The sign-in client](/mcp/#the-sign-in-client)) + and sets `mcp.oauth.clientId` (and `mcp.oauth.callbackPort` if it is not `33418`). Until + then MCP clients cannot sign in on their own. +- **Users reconnect their agents.** Re-add the server with the command on the UI's + **Settings** page (`claude mcp add … --client-id --callback-port `), then run + `/mcp` in Claude Code, select it and choose **Authenticate**. Agents that called the + `authenticate` tool no longer find it. +- **Drop `MCP_SESSION_TTL_SECONDS`** from `mcp.extraEnv` if you set it; it is ignored. + ### Upgrading to group-only access **Breaking:** Apps restrict who may open them by Keycloak group only. `spec.access.users`