Skip to content

feat(examples): add notes-oauth-keycloak end-to-end example - #44

Merged
IBJunior merged 2 commits into
mainfrom
feat/examples-notes-oauth-keycloak
Nov 8, 2026
Merged

IBJunior merged 2 commits into
mainfrom
feat/examples-notes-oauth-keycloak

Conversation

@IBJunior

@IBJunior IBJunior commented Nov 8, 2026

Copy link
Copy Markdown
Member

Summary

Adds an examples/ folder with the first end-to-end example, notes-oauth-keycloak: the notes server from create-mcp-server --oauth, a local Keycloak that is fully configured from a realm file (no console clicks), and notes kept per signed-in user.

  • Commit 1 is the unmodified output of npx @agentailor/create-mcp-server@0.11.0 --name=notes-oauth-keycloak --oauth --no-git --no-skills.
  • Commit 2 adds the example's changes, each marked // example: in code and listed in the example README:
    • auth.ts forwards the token's sub and preferred_username in AuthInfo.extra, and requires the mcp:tools scope.
    • New identity.ts (callerFrom(ctx) reads ctx.http.authInfo). The store, tools and resources partition notes by sub. Adds a whoami tool.
    • docker-compose.yml runs Keycloak 26.4.7, imports keycloak/mcp-realm-realm.json on start, and has a healthcheck for up --wait.
    • npm run token and npm run smoke.
    • Docs: examples/README.md, plus an Examples section in the root README.md and AGENTS.md.
    • .github/workflows/examples.yml runs the example's tests and smoke test. It also triggers on src/templates/**, so template drift fails on the PR that causes it.

Realm details worth knowing

  • The import file must be named <realm>-realm.json, or Keycloak refuses it.
  • Requests from the host reach Keycloak from the Docker bridge gateway, not localhost. The compose network therefore has a fixed subnet, and the Trusted Hosts registration policy trusts its gateway. Without that, clients that register themselves (Dynamic Client Registration) get a 403.
  • Users imported from a file get no roles unless the file lists them. Self-registered clients also have full scope disabled. So users are given default-roles-mcp-realm, and the offline_access scope is mapped to its role. Without this, the Inspector's token exchange fails with "Offline tokens not allowed".

Testing

  • Example unit tests: 12 pass (3 files), including new per-user isolation and identity tests.
  • npm run smoke against a live Keycloak passes 10 of 10. It checks: the metadata points at the issuer, requests without a token get 401 with a resource_metadata challenge, a forged token gets 401, a user can get an offline token, whoami returns alice with mcp:tools, and bob can't list or read alice's notes.
  • MCP Inspector v2 manually: it registers itself, signs in as bob, connects, and lists tools, prompts and resources.
  • Root lint and tests are unchanged (404 pass). src/ isn't touched.
  • Not yet tested: VS Code and Cursor sign-in. The new examples.yml workflow runs for the first time on this PR.

🤖 Generated with Claude Code

IBJunior and others added 2 commits November 8, 2026 19:19
… 0.11.0

Unmodified output of:
npx @agentailor/create-mcp-server@0.11.0 --name=notes-oauth-keycloak --oauth --no-git --no-skills

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds a local Keycloak (docker compose, realm imported from a file) and
the changes that make the scaffolded --oauth server a complete example:

- auth.ts forwards the token's sub/username and requires mcp:tools
- notes are kept per user, keyed by sub; adds a whoami tool
- get-token and smoke scripts; smoke checks 401s, offline tokens,
  identity and per-user isolation
- realm: users get default roles and offline_access is mapped to its
  role, so self-registered clients such as the Inspector can sign in;
  the compose network has a fixed subnet so its gateway can be trusted
  for Dynamic Client Registration
- examples index, README/AGENTS.md docs, and an examples CI workflow
  that also runs on template changes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@IBJunior
IBJunior merged commit 2905333 into main Nov 8, 2026
3 checks passed
@IBJunior
IBJunior deleted the feat/examples-notes-oauth-keycloak branch November 8, 2026 20:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant