Skip to content

🤖mcp server oauth - #2646

Open
AntoLC wants to merge 6 commits into
mainfrom
feat/mcp-server-oauth
Open

🤖mcp server oauth#2646
AntoLC wants to merge 6 commits into
mainfrom
feat/mcp-server-oauth

Conversation

@AntoLC

@AntoLC AntoLC commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator

Purpose

This PR lays the groundwork for an MCP server for Docs (docs-mcp): a remote MCP server exposing three tools backed by the Django API:

  • search_documents
  • read_document
  • create_document

It is an OAuth-protected resource server: MCP clients authenticate the user against Keycloak (Authorization Code + PKCE, public docs-mcp-client), and the server forwards that same access token, unchanged, to Django.

Django stays the sole authority on document permissions — ResourceServerAuthentication (django-lasuite) introspects the token on every call and the existing DocumentViewSet permissions/querysets decide access.

There is deliberately no RFC 8693 token exchange: the TypeScript server (Express + @modelcontextprotocol/sdk, stateless Streamable HTTP) holds no credentials of its own, only reading Keycloak's public JWKS to verify signatures.

We're eager to hear from people using it and to see it augmented — more tools, richer document operations, DCR, token exchange — so please try it and tell us what you need.

How to do

To start the mcp server, as usual:

  • make build
  • make run

When your MCP is running, you can check that everything is fine by checking it with mcp-inspector, by running:

  • make mcp-inspector

    docs/Makefile

    Lines 528 to 530 in b7f3bec

    mcp-inspector: ## Launch the MCP Inspector against docs-mcp with the client/scopes pre-filled
    cd src/frontend/servers/mcp && yarn mcp-inspector
    .PHONY: mcp-inspector

You can use it with your favorite LLM (Claude, Codex, Cursor...), here some helpers to plug it easily:

  • make mcp-claude
  • make mcp-codex

More here:

docs/Makefile

Lines 462 to 527 in b7f3bec

mcp-claude: ## Connect Claude Code to docs-mcp and launch it (authenticate with `/mcp` on first use)
@test -f .mcp.json || printf '%s\n' \
'{' \
' "mcpServers": {' \
' "docs-mcp": {' \
' "type": "http",' \
' "url": "http://localhost:4455/mcp",' \
' "oauth": {' \
' "clientId": "docs-mcp-client",' \
' "callbackPort": 8090' \
' }' \
' }' \
' }' \
'}' \
> .mcp.json
claude
.PHONY: mcp-claude
mcp-codex: ## Connect Codex CLI to docs-mcp and launch it (authenticate with `codex mcp login docs-mcp` on first use)
@mkdir -p .codex
@test -f .codex/config.toml || printf '%s\n' \
'# docs-mcp: fixes the local OAuth callback port so it can be pre-registered as a' \
'# Keycloak redirect URI (docker/auth/realm.json, docs-mcp-client). See' \
'# documentation/mcp_server.md.' \
'mcp_oauth_callback_port = 8091' \
> .codex/config.toml
codex mcp add docs-mcp --url http://localhost:4455/mcp --oauth-client-id docs-mcp-client 2>/dev/null || true
codex
.PHONY: mcp-codex
mcp-cursor: ## Open this project in Cursor with docs-mcp connected (authenticate docs-mcp from Cursor's MCP settings on first use)
@mkdir -p .cursor
@test -f .cursor/mcp.json || printf '%s\n' \
'{' \
' "mcpServers": {' \
' "docs-mcp": {' \
' "url": "http://localhost:4455/mcp",' \
' "auth": {' \
' "CLIENT_ID": "docs-mcp-client"' \
' }' \
' }' \
' }' \
'}' \
> .cursor/mcp.json
cursor .
.PHONY: mcp-cursor
mcp-gemini: ## Connect Gemini CLI to docs-mcp and launch it (approve the OAuth prompt on first use)
@mkdir -p .gemini
@test -f .gemini/settings.json || printf '%s\n' \
'{' \
' "mcpServers": {' \
' "docs-mcp": {' \
' "httpUrl": "http://localhost:4455/mcp",' \
' "oauth": {' \
' "enabled": true,' \
' "clientId": "docs-mcp-client",' \
' "redirectUri": "http://localhost:8092/oauth/callback"' \
' }' \
' }' \
' }' \
'}' \
> .gemini/settings.json
gemini
.PHONY: mcp-gemini

Extensive documentation here:

https://github.com/suitenumerique/docs/blob/feat/mcp-server-oauth/documentation/mcp_server.md

@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Size Change: 0 B

Total Size: 5.94 MB

📦 View Changed
Filename Size Change
apps/impress/out/_next/static/d9a7aec3/_buildManifest.js 0 B -698 B (removed) 🏆
apps/impress/out/_next/static/f4fa8e54/_buildManifest.js 698 B +698 B (new file) 🆕

compressed-size-action

We added a basic MCP API to the backend.
This API is designed to facilitate integration with external
systems, providing a secure and efficient way to
access document data.

API endpoints include:
- POST /api/v1.0/mcp/documents/search  -> search_documents
- GET  /api/v1.0/mcp/documents/<id>    -> read_document
- POST /api/v1.0/mcp/documents         -> create_document
Add to the dev realm:

- `docs-mcp-client`, the public PKCE client MCP clients authenticate
  with, with optional scopes `docs:documents:{search,read,create}` and
  `docs-mcp`.
- `docs-api`, the confidential client Django uses as introspection
  credentials (`OIDC_RS_CLIENT_ID/SECRET`).
- the `docs:documents:*` consent scopes and a `docs-mcp` audience-mapper
  scope that stamps the `docs-mcp` audience only when requested.

Point the resource-server env at the `impress` realm and the new
clients, allow the `docs-mcp-client` audience, and grant users
`offline_access` for refresh tokens.
@AntoLC
AntoLC force-pushed the feat/mcp-server-oauth branch from b7f3bec to f978612 Compare September 6, 2026 12:20
@AntoLC
AntoLC requested review from jmaupetit, lunika and qbey September 6, 2026 12:30
@AntoLC AntoLC changed the title ✨mcp server oauth 🤖mcp server oauth Sep 6, 2026
Add `src/frontend/servers/mcp`, a stateless TypeScript (Express +
`@modelcontextprotocol/sdk`) MCP server exposing `search_documents`,
`read_document` and `create_document` over Streamable HTTP.

It is an OAuth resource server holding no credentials of its own: it
verifies the caller's Keycloak token against the realm JWKS
(`src/auth/jwtVerifier.ts`), checks the `docs-mcp` audience, then
forwards the same token unchanged to Django's
`/api/v1.0/mcp/documents/*` endpoints (`src/docsApiClient.ts`),
which stays the sole authority on document permissions.
No token exchange.

Ship the `mcp-development` compose service, its `env.d/development/mcp`
env file, a `build-mcp` Makefile helper, and
`documentation/mcp_server.md` describing the full flow and
how to connect MCP clients.
Add a `yarn mcp-inspector` script that launches
`@modelcontextprotocol/inspector` against the local
docs-mcp server, with a checked-in config pre-filling
the URL and the `docs-mcp-client`
OAuth client and scopes so there is nothing to type
in by hand.
We added some helper commands to the Makefile to
facilitate the development of the docs-mcp.
These commands allow developers to easily
connect to the docs-mcp server with your
favorite LLM.
You can start the MCP Inspector with
`make mcp-inspector` to connect to the docs-mcp server and
inspect the different endpoints and their responses.
Wire up the corresponding build-and-push-mcp jobs
in the Docker Hub and GHCR workflows.
@AntoLC
AntoLC force-pushed the feat/mcp-server-oauth branch from f978612 to d6420cc Compare September 6, 2026 12:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant