strapi-oauth-mcp-manager 0.1.4 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/LICENSE +21 -0
  3. package/README.md +211 -232
  4. package/dist/_chunks/App-Ddu3EpO8.mjs +397 -0
  5. package/dist/_chunks/App-WKeHpRl7.js +397 -0
  6. package/dist/_chunks/{en-B4KWt_jN.js → en-Dw8Cn8uZ.js} +3 -1
  7. package/dist/_chunks/en-UyIAE3Kz.mjs +6 -0
  8. package/dist/_chunks/{index-B2ShbPnj.js → index-BPOlIxN_.js} +4 -3
  9. package/dist/_chunks/{index-DzBaU9Fw.mjs → index-BrZfQfFg.mjs} +4 -3
  10. package/dist/admin/index.js +1 -1
  11. package/dist/admin/index.mjs +1 -1
  12. package/dist/server/index.js +1530 -432
  13. package/dist/server/index.mjs +1531 -433
  14. package/dist/server/src/bootstrap.d.ts +1 -0
  15. package/dist/server/src/config/index.d.ts +33 -2
  16. package/dist/server/src/content-types/index.d.ts +57 -10
  17. package/dist/server/src/controllers/admin.d.ts +16 -0
  18. package/dist/server/src/controllers/index.d.ts +17 -1
  19. package/dist/server/src/controllers/oauth.d.ts +21 -19
  20. package/dist/server/src/destroy.d.ts +1 -4
  21. package/dist/server/src/index.d.ts +176 -22
  22. package/dist/server/src/middlewares/index.d.ts +1 -1
  23. package/dist/server/src/middlewares/mcp-oauth.d.ts +10 -13
  24. package/dist/server/src/permissions.d.ts +3 -0
  25. package/dist/server/src/register.d.ts +1 -1
  26. package/dist/server/src/routes/admin/index.d.ts +13 -1
  27. package/dist/server/src/routes/index.d.ts +13 -1
  28. package/dist/server/src/services/index.d.ts +73 -3
  29. package/dist/server/src/services/oauth.d.ts +145 -13
  30. package/dist/server/src/utils/crypto.d.ts +20 -0
  31. package/dist/server/src/utils/url.d.ts +32 -0
  32. package/dist/server/src/views/authorize-page.d.ts +31 -0
  33. package/package.json +28 -15
  34. package/dist/_chunks/App-CjW3NftW.mjs +0 -23
  35. package/dist/_chunks/App-DsMhfKkM.js +0 -23
  36. package/dist/_chunks/en-Byx4XI2L.mjs +0 -4
  37. package/dist/admin/src/utils/getTranslation.d.ts +0 -2
package/CHANGELOG.md ADDED
@@ -0,0 +1,33 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0
4
+
5
+ A rewrite that adds OAuth sign-in to the MCP server built into Strapi 5.47+ (`/mcp`).
6
+
7
+ ### Added
8
+
9
+ - OAuth 2.1 authorization server for `/mcp`: protected resource metadata (RFC 9728), authorization server metadata (RFC 8414), dynamic client registration (RFC 7591), PKCE with S256 (RFC 7636), token revocation (RFC 7009) and refresh token rotation.
10
+ - Consent page where users sign in with their Strapi admin account and pick one of their own admin tokens. Each session gets exactly that token's permissions.
11
+ - Optional admin token mapping per client. A mapped client always uses its token, and only the token's owner can approve it.
12
+ - Optional "All of my permissions" access, behind `allowUserPermissions` (off by default).
13
+ - Revocation at every level: deactivating a user, deleting or regenerating a token, revoking a session, revoking all sessions for a user, turning off a client, or changing a client's mapped token.
14
+ - **MCP OAuth** admin page with connection details, connected sessions and client management, gated by the new **Manage MCP OAuth clients and grants** permission.
15
+ - Plugin configuration: `accessTokenTtl`, `refreshTokenTtl`, `authorizationCodeTtl`, `refreshTokenReuseWindow`, `dynamicClientRegistration`, `allowUserPermissions` and `cleanupIntervalMs`.
16
+ - Refresh token reuse detection: a rotated refresh token used again after `refreshTokenReuseWindow` (10 seconds) revokes the session. Rotation is atomic, so concurrent refreshes with one token can't both succeed.
17
+ - Warning when OAuth is served over plain `http` on a host other than `localhost`.
18
+ - Confirmation before deleting a client or revoking all of a user's sessions, and an error state with retry when the admin page can't load.
19
+ - End-to-end test suites (`npm run test:e2e`).
20
+
21
+ ### Changed
22
+
23
+ - Discovery documents moved to the server root (`/.well-known/...`).
24
+ - OAuth codes and tokens are stored only as SHA-256 hashes.
25
+ - The OAuth client content type is no longer shown in the Content Manager.
26
+
27
+ ### Breaking changes
28
+
29
+ - Requires Strapi 5.47.0 or later with `server.mcp.enabled: true`.
30
+ - Only `/mcp` is protected. Routes matching `/api/*/mcp` are no longer handled.
31
+ - The OAuth client, code and token tables changed. Existing tokens stop working, and clients reconnect through the consent page.
32
+ - The `strapiApiToken` field on clients is removed. Map an admin token to the client instead.
33
+ - The `/api/strapi-oauth-mcp-manager/.well-known/*` routes are removed.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 Paul Bratslavsky
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,319 +1,298 @@
1
1
  # Strapi OAuth MCP Manager
2
2
 
3
- Centralized OAuth 2.0 authentication manager for Strapi MCP (Model Context Protocol) plugins. Enables ChatGPT, Claude, and other AI assistants to securely authenticate with your Strapi MCP endpoints.
3
+ Adds OAuth sign-in to [Strapi's built-in MCP server](https://docs.strapi.io/cms/features/strapi-mcp-server), so Claude, ChatGPT, Cursor and other MCP clients can connect without a pasted token.
4
4
 
5
- ## Features
5
+ [![npm](https://img.shields.io/npm/v/strapi-oauth-mcp-manager)](https://www.npmjs.com/package/strapi-oauth-mcp-manager) ![license](https://img.shields.io/npm/l/strapi-oauth-mcp-manager)
6
6
 
7
- - **Centralized OAuth 2.0** - Single authentication layer for all MCP plugins
8
- - **RFC Compliant** - Implements RFC 6749, RFC 8414, and RFC 9728
9
- - **Dual Authentication** - Supports both OAuth tokens and Strapi API tokens
10
- - **ChatGPT Ready** - Works with ChatGPT's MCP integration out of the box
11
- - **Claude Compatible** - Supports direct API token authentication
12
- - **Admin Management** - Manage OAuth clients through Strapi admin panel
13
- - **Wildcard Redirects** - Supports wildcard patterns in redirect URIs
7
+ ---
14
8
 
15
- ## Installation
9
+ ## Highlights
16
10
 
17
- ```bash
18
- npm install strapi-oauth-mcp-manager
19
- ```
11
+ - **One URL to connect.** Paste `https://your-strapi.com/mcp` into an MCP client, sign in with your Strapi admin account, and pick what the client can access.
12
+ - **Permissions come from admin tokens.** Each connection uses an admin token you create in **Settings → Admin Tokens**, with exactly the permissions you give it.
13
+ - **Easy to revoke.** Deactivate a user, delete a token, or revoke a session, and access stops on the next request.
14
+ - **Uses Strapi's own MCP server.** Every tool on `/mcp` works unchanged, including the content tools Strapi generates and any tool registered with `strapi.ai.mcp.registerTool`.
15
+ - **Standards-based.** Implements the [MCP authorization spec](https://modelcontextprotocol.io/specification/latest/basic/authorization): discovery, dynamic client registration, PKCE, refresh and revocation.
20
16
 
21
- ## Configuration
17
+ ---
22
18
 
23
- Add the plugin to your `config/plugins.ts`:
19
+ ## Overview
24
20
 
25
- ```typescript
26
- export default () => ({
27
- 'strapi-oauth-mcp-manager': {
28
- enabled: true,
29
- },
30
- // Your other MCP plugins...
31
- 'yt-transcript-strapi-plugin': {
32
- enabled: true,
33
- },
34
- 'strapi-content-mcp': {
35
- enabled: true,
36
- },
37
- });
38
- ```
21
+ Strapi 5.47 added an MCP server at `/mcp`. Out of the box it accepts only admin tokens pasted into each client. Many clients, such as claude.ai connectors and ChatGPT, expect OAuth instead.
39
22
 
40
- ---
23
+ This plugin makes Strapi an OAuth authorization server for `/mcp`. When a client connects, the user signs in with their Strapi admin account and chooses one of their admin tokens. The client then gets short-lived OAuth tokens, and every request reaches Strapi's MCP server with that admin token's permissions.
41
24
 
42
- ## Quick Start: ChatGPT Setup
25
+ <img src="https://raw.githubusercontent.com/PaulBratslavsky/strapi-oauth-mcp-manager/main/docs/sign-in-page.png" alt="Consent page where the user picks which admin token the client can use" width="480">
43
26
 
44
- ### Step 1: Create a Strapi API Token
27
+ ---
45
28
 
46
- 1. Go to **Strapi Admin** → **Settings** → **API Tokens**
47
- 2. Click **Create new API Token**
48
- 3. Configure:
49
- - **Name**: `ChatGPT MCP Access`
50
- - **Token type**: `Full access` (or custom with specific permissions)
51
- - **Token duration**: `Unlimited` (recommended) or set expiry
52
- 4. Click **Save** and **copy the token** (you won't see it again)
29
+ ## Quick Start
53
30
 
54
- ### Step 2: Create an OAuth Client in Strapi
31
+ 1. Install the plugin in a Strapi 5.47+ project:
55
32
 
56
- 1. Go to **Strapi Admin** → **Content Manager** → **MCP OAuth Client**
57
- 2. Click **Create new entry**
58
- 3. Fill in the fields:
33
+ ```bash
34
+ npm install strapi-oauth-mcp-manager
35
+ ```
59
36
 
60
- | Field | Value |
61
- |-------|-------|
62
- | **name** | `ChatGPT` |
63
- | **clientId** | `chatgpt` |
64
- | **clientSecret** | Choose a secure secret (e.g., `my-super-secret-key-123`) |
65
- | **redirectUris** | `https://chatgpt.com/connector_platform_oauth_redirect` |
66
- | **strapiApiToken** | Paste the API token from Step 1 |
67
- | **active** | `true` |
37
+ 2. Turn on Strapi's MCP server in `config/server.ts`:
68
38
 
69
- 4. Click **Save**
39
+ ```typescript
40
+ export default ({ env }) => ({
41
+ host: env('HOST', '0.0.0.0'),
42
+ port: env.int('PORT', 1337),
43
+ app: { keys: env.array('APP_KEYS') },
44
+ mcp: { enabled: true },
45
+ });
46
+ ```
70
47
 
71
- ### Step 3: Configure ChatGPT
48
+ 3. Enable the plugin in `config/plugins.ts`, then restart Strapi:
72
49
 
73
- 1. Go to [ChatGPT](https://chatgpt.com)
74
- 2. Click your profile → **Settings** → **Connected Apps** → **Add App**
75
- 3. Fill in the form:
50
+ ```typescript
51
+ export default () => ({
52
+ 'strapi-oauth-mcp-manager': { enabled: true },
53
+ });
54
+ ```
76
55
 
77
- | Field | Value |
78
- |-------|-------|
79
- | **Name** | `Your App Name` (e.g., "YT Transcripts") |
80
- | **Description** | What the app does |
81
- | **MCP Server URL** | `https://your-strapi-domain.com/api/yt-transcript-strapi-plugin/mcp` |
82
- | **Authentication** | `OAuth` |
83
- | **Client ID** | `chatgpt` (from Step 2) |
84
- | **Client Secret** | Your secret from Step 2 |
56
+ 4. In the Strapi admin panel, go to **Settings → Admin Tokens** and create a token with the permissions the client should have, for example read and update on your content types.
85
57
 
86
- 4. Click **Save**
58
+ 5. Connect a client. For Claude Code:
87
59
 
88
- ### Step 4: Authorize
60
+ ```bash
61
+ claude mcp add --transport http strapi http://localhost:1337/mcp
62
+ ```
89
63
 
90
- When you first use the MCP tools in ChatGPT, it will redirect you to Strapi to authorize. Click **Authorize** to complete the OAuth flow.
64
+ Run `/mcp`, select `strapi`, and choose **Authenticate**. In the browser, sign in, choose the token from step 4, and select **Authorize**.
91
65
 
92
66
  ---
93
67
 
94
- ## Quick Start: Claude Desktop Setup
68
+ ## Installation
95
69
 
96
- Claude Desktop uses direct Strapi API tokens (no OAuth flow needed).
70
+ **npm**
97
71
 
98
- ### Step 1: Create a Strapi API Token
72
+ ```bash
73
+ npm install strapi-oauth-mcp-manager
74
+ ```
99
75
 
100
- Same as ChatGPT Step 1 above.
76
+ **yarn**
101
77
 
102
- ### Step 2: Configure Claude Desktop
78
+ ```bash
79
+ yarn add strapi-oauth-mcp-manager
80
+ ```
103
81
 
104
- Edit your Claude Desktop config file:
82
+ Requirements:
105
83
 
106
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
107
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
84
+ - Strapi 5.47.0 or later
85
+ - `admin.secrets.encryptionKey` set in `config/admin.ts` (new projects read it from `ENCRYPTION_KEY` in `.env`)
86
+ - Node.js 20 or later
108
87
 
109
- ```json
110
- {
111
- "mcpServers": {
112
- "yt-transcript": {
113
- "command": "npx",
114
- "args": [
115
- "mcp-remote",
116
- "https://your-strapi-domain.com/api/yt-transcript-strapi-plugin/mcp",
117
- "--header",
118
- "Authorization: Bearer YOUR_STRAPI_API_TOKEN"
119
- ]
120
- },
121
- "strapi-content": {
122
- "command": "npx",
123
- "args": [
124
- "mcp-remote",
125
- "https://your-strapi-domain.com/api/strapi-content-mcp/mcp",
126
- "--header",
127
- "Authorization: Bearer YOUR_STRAPI_API_TOKEN"
128
- ]
129
- }
130
- }
131
- }
132
- ```
88
+ > **In production,** set `url` in `config/server.ts` to your public HTTPS address, or set `proxy: true` behind a reverse proxy. Clients use this address to find the sign-in page.
133
89
 
134
- Replace:
135
- - `your-strapi-domain.com` with your actual Strapi URL
136
- - `YOUR_STRAPI_API_TOKEN` with the token from Step 1
90
+ ---
137
91
 
138
- ### Step 3: Restart Claude Desktop
92
+ ## Access and permissions
139
93
 
140
- Quit and reopen Claude Desktop to load the new configuration.
94
+ Every connection runs on an **admin token owned by the person who approved it**.
141
95
 
142
- ---
96
+ 1. A user creates one or more admin tokens in **Settings → Admin Tokens**, each with its own permissions and expiry. For example: "Claude – content editor" and "ChatGPT – read only".
97
+ 2. When a client connects, the user signs in and picks one of **their own** tokens. They can't see or pick anyone else's.
98
+ 3. The client can do exactly what that token allows. Strapi already caps a token at its owner's role, so nobody can grant more than they have.
143
99
 
144
- ## How It Works
100
+ Different people can connect the same client with different access. An editor might connect with a publishing token, and an author with a draft-only token.
145
101
 
146
- ### Architecture
102
+ ### Map a token to a client
147
103
 
148
- ```
149
- ┌─────────────────────────────────────────────────────────────┐
150
- │ Strapi Application │
151
- │ │
152
- │ ┌─────────────────────────────────────────────────────┐ │
153
- │ │ strapi-oauth-mcp-manager │ │
154
- │ │ ┌───────────────┐ ┌───────────────────────────┐ │ │
155
- │ │ │ OAuth │ │ Global Auth Middleware │ │ │
156
- │ │ │ Endpoints │ │ (protects all registered │ │ │
157
- │ │ │ /authorize │ │ MCP endpoints) │ │ │
158
- │ │ │ /token │ └───────────────────────────┘ │ │
159
- │ │ └───────────────┘ │ │
160
- │ └─────────────────────────────────────────────────────┘ │
161
- │ │ │
162
- │ ┌──────────────────┼──────────────────┐ │
163
- │ ▼ ▼ ▼ │
164
- │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
165
- │ │ MCP Plugin │ │ MCP Plugin │ │ MCP Plugin │ │
166
- │ │ A │ │ B │ │ C │ │
167
- │ │ (registers) │ │ (registers) │ │ (registers) │ │
168
- │ └─────────────┘ └─────────────┘ └─────────────┘ │
169
- └─────────────────────────────────────────────────────────────┘
170
- ```
104
+ To fix which token a client uses, map the token to the client:
171
105
 
172
- ### For MCP Plugin Developers
106
+ 1. Create the token in **Settings → Admin Tokens**, for example "ChatGPT – read only".
107
+ 2. Open **MCP OAuth** in the sidebar. When you add a client, choose the token under **Admin token**. For an existing client, including one that registered itself, choose it in the client's **Admin token** column.
173
108
 
174
- MCP plugins register with the OAuth manager to gain authentication support:
109
+ A mapped client skips the picker. The consent page shows the mapped token, and every session uses it.
175
110
 
176
- ```typescript
177
- // In your plugin's bootstrap.ts
178
- const oauthPlugin = strapi.plugin('strapi-oauth-mcp-manager');
179
-
180
- if (oauthPlugin) {
181
- await oauthPlugin.service('endpoint').register({
182
- name: 'My MCP Plugin',
183
- pluginId: 'my-mcp-plugin',
184
- path: '/api/my-mcp-plugin/mcp',
185
- description: 'MCP endpoint for my tools',
186
- });
187
- }
188
- ```
111
+ - **Only the token's owner can connect a mapped client.** Anyone else who signs in gets "Only that token's owner can connect it", so a mapped client never gives someone more access than they already have.
112
+ - **You can map only tokens you own.** A token owned by someone else shows in the column, but you can only replace it or clear it.
113
+ - **Changing or clearing the mapping ends the client's existing sessions.** Choose **Picked when connecting** to go back to the picker.
114
+ - **If the mapped token is deleted,** the client refuses to connect until you choose a new token. It doesn't fall back to the picker.
189
115
 
190
- ### Authentication Flow
116
+ ### Let users create tokens
191
117
 
192
- ```
193
- ChatGPT/OAuth Client Strapi
194
- │ │
195
- │ 1. Request MCP endpoint │
196
- │ ─────────────────────────────────>│
197
- │ │
198
- │ 2. 401 + WWW-Authenticate header │
199
- │ <─────────────────────────────────│
200
- │ │
201
- │ 3. Discover OAuth via .well-known │
202
- │ ─────────────────────────────────>│
203
- │ │
204
- │ 4. Redirect to /authorize │
205
- │ ─────────────────────────────────>│
206
- │ │
207
- │ 5. User authorizes, get code │
208
- │ <─────────────────────────────────│
209
- │ │
210
- │ 6. Exchange code for token │
211
- │ ─────────────────────────────────>│
212
- │ │
213
- │ 7. Access token returned │
214
- │ <─────────────────────────────────│
215
- │ │
216
- │ 8. MCP requests with Bearer token │
217
- │ ─────────────────────────────────>│
218
- │ │
219
- │ 9. MCP response │
220
- │ <─────────────────────────────────│
221
- ```
118
+ Super Admins can create admin tokens by default. For other roles, go to **Settings → Administration Panel → Roles**, open the role, and on the **Settings** tab enable **Admin Tokens** (access, create, read, update, regenerate, delete). Users only ever see and manage their own tokens.
119
+
120
+ ### Use full user permissions instead
121
+
122
+ To let users connect without creating a token, set `allowUserPermissions: true` (see [Configuration](#configuration)). The consent page then also offers **All of my permissions**, which creates a token carrying everything the user's role allows. It's off by default.
222
123
 
223
124
  ---
224
125
 
225
- ## OAuth Endpoints
126
+ ## Revoking access
226
127
 
227
- | Endpoint | URL |
228
- |----------|-----|
229
- | Authorization | `/api/strapi-oauth-mcp-manager/oauth/authorize` |
230
- | Token | `/api/strapi-oauth-mcp-manager/oauth/token` |
231
- | Discovery (RFC 8414) | `/api/strapi-oauth-mcp-manager/.well-known/oauth-authorization-server` |
232
- | Protected Resource (RFC 9728) | `/api/strapi-oauth-mcp-manager/.well-known/oauth-protected-resource` |
128
+ | To stop… | Do this | Effect |
129
+ |---|---|---|
130
+ | Everything a person connected | Deactivate or delete their admin user | All their sessions stop immediately |
131
+ | A person's sessions, keeping their account | The person icon next to any of their sessions on the **MCP OAuth** page | All sessions they approved end |
132
+ | Everything using one access level | Delete the token in **Settings → Admin Tokens** | All sessions on that token end |
133
+ | The same, but keep the token's settings | **Regenerate** the token | All sessions on that token end |
134
+ | One connection | Revoke the session on the **MCP OAuth** page | Only that session ends |
135
+ | One app | Turn off or delete the client on the **MCP OAuth** page | All its sessions end, and it can't connect again while off |
136
+ | One app's access level | Change the client's mapped token | Its sessions end; new ones use the new token |
137
+
138
+ Revoking a session never deletes the admin token the user picked.
233
139
 
234
140
  ---
235
141
 
236
- ## Content Types
142
+ ## Connecting clients
143
+
144
+ Every client needs only the MCP server URL: `https://your-strapi.com/mcp`.
145
+
146
+ | Client | How to connect |
147
+ |---|---|
148
+ | Claude Code | `claude mcp add --transport http strapi https://your-strapi.com/mcp`, then `/mcp` → **Authenticate** |
149
+ | claude.ai and Claude Desktop | **Settings → Connectors → Add custom connector**. Leave the client ID and secret empty. |
150
+ | Cursor, VS Code, MCP Inspector | Add an HTTP MCP server with the URL. The client opens the sign-in page on first use. |
151
+ | ChatGPT | Add a connector with the URL and OAuth authentication. If it asks for a client ID and secret, create them as described below. |
237
152
 
238
- The plugin creates these content types:
153
+ ### Clients that need a client ID and secret
239
154
 
240
- | Content Type | Purpose |
241
- |--------------|---------|
242
- | `mcp-oauth-client` | OAuth client configurations |
243
- | `mcp-oauth-code` | Authorization codes (temporary) |
244
- | `mcp-oauth-token` | Access and refresh tokens |
245
- | `mcp-endpoint` | Registered MCP endpoints |
155
+ 1. In Strapi, open **MCP OAuth** in the sidebar and choose **Add client**.
156
+ 2. Enter a name and the client's redirect URI, for example `https://chatgpt.com/connector_platform_oauth_redirect`.
157
+ 3. Optionally choose an **Admin token** so this client always uses it (see [Map a token to a client](#map-a-token-to-a-client)).
158
+ 4. Copy the client ID and secret into the client. The secret is shown only once.
159
+
160
+ ### Clients that can't do OAuth
161
+
162
+ Send an admin token as `Authorization: Bearer <token>`. The plugin passes it to Strapi's MCP server unchanged.
246
163
 
247
164
  ---
248
165
 
249
- ## OAuth Client Configuration
166
+ ## Signing in
167
+
168
+ The consent page accepts a **Strapi admin email and password**. Only admin accounts can connect, because Strapi's MCP server works with admin permissions.
169
+
170
+ These sign-in methods aren't supported:
250
171
 
251
- ### Required Fields
172
+ - **Admin SSO.** Admins who sign in to Strapi only through an SSO provider have no password to enter.
173
+ - **Users & Permissions providers** such as GitHub or Google. These sign in front-end users, who have no admin permissions and can't use the MCP server.
252
174
 
253
- | Field | Type | Description |
254
- |-------|------|-------------|
255
- | `name` | string | Display name for the client |
256
- | `clientId` | string | Unique identifier (e.g., `chatgpt`) |
257
- | `clientSecret` | string | Secret for token exchange |
258
- | `redirectUris` | string[] | Allowed redirect URIs after authorization |
259
- | `strapiApiToken` | string | Strapi API token to use for authenticated requests |
260
- | `active` | boolean | Whether the client is active |
175
+ ---
261
176
 
262
- ### Redirect URIs
177
+ ## How it works
178
+
179
+ ```mermaid
180
+ sequenceDiagram
181
+ participant C as MCP client
182
+ participant P as This plugin
183
+ participant S as Strapi MCP server
184
+ participant U as Admin (browser)
185
+
186
+ C->>P: POST /mcp without a token
187
+ P-->>C: 401 with a link to the discovery document
188
+ C->>P: Discover endpoints and register
189
+ C->>U: Open the consent page
190
+ U->>P: Sign in, pick an admin token, approve
191
+ P-->>C: Authorization code
192
+ C->>P: Exchange the code for tokens
193
+ C->>P: POST /mcp with the access token
194
+ P->>S: Same request with the chosen admin token
195
+ S-->>C: MCP response
196
+ ```
263
197
 
264
- Common redirect URIs:
198
+ - Access tokens last 1 hour. Refresh tokens last 30 days and are replaced every time they're used. If two requests use the same refresh token at once, only one succeeds.
199
+ - On every request the plugin checks that the session exists, the admin token still exists and hasn't been regenerated, and the approving user is still active.
200
+ - OAuth tokens and authorization codes are stored only as SHA-256 hashes. Admin token keys stay in Strapi, encrypted with `admin.secrets.encryptionKey`.
265
201
 
266
- | Client | Redirect URI |
267
- |--------|--------------|
268
- | ChatGPT | `https://chatgpt.com/connector_platform_oauth_redirect` |
269
- | Custom OAuth | Your app's callback URL |
202
+ ### Endpoints
270
203
 
271
- Wildcard patterns are supported (e.g., `https://*.example.com/callback`).
204
+ | Purpose | Path |
205
+ |---|---|
206
+ | Protected resource metadata | `/.well-known/oauth-protected-resource/mcp` |
207
+ | Authorization server metadata | `/.well-known/oauth-authorization-server` |
208
+ | Consent page | `/api/strapi-oauth-mcp-manager/oauth/authorize` |
209
+ | Token | `/api/strapi-oauth-mcp-manager/oauth/token` |
210
+ | Client registration | `/api/strapi-oauth-mcp-manager/oauth/register` |
211
+ | Revocation | `/api/strapi-oauth-mcp-manager/oauth/revoke` |
272
212
 
273
213
  ---
274
214
 
275
- ## Troubleshooting
215
+ ## Configuration
276
216
 
277
- ### "Invalid redirect_uri" Error
217
+ All options are optional. Defaults are shown.
278
218
 
279
- Make sure the redirect URI in your OAuth client exactly matches what the client sends. For ChatGPT, use:
280
- ```
281
- https://chatgpt.com/connector_platform_oauth_redirect
219
+ ```typescript
220
+ export default () => ({
221
+ 'strapi-oauth-mcp-manager': {
222
+ enabled: true,
223
+ config: {
224
+ accessTokenTtl: 3600, // seconds
225
+ refreshTokenTtl: 2592000, // seconds (30 days)
226
+ authorizationCodeTtl: 600, // seconds
227
+ refreshTokenReuseWindow: 10, // seconds a reused refresh token counts as a retry before the session is revoked
228
+ dynamicClientRegistration: true, // false = only clients added in the admin panel
229
+ allowUserPermissions: false, // true = also offer "All of my permissions" on the consent page
230
+ cleanupIntervalMs: 3600000, // how often expired data is removed; 0 turns it off
231
+ },
232
+ },
233
+ });
282
234
  ```
283
235
 
284
- ### "Session expired" Error
236
+ The **MCP OAuth** admin page requires the **Manage MCP OAuth clients and grants** permission, on the **Plugins** tab of each role under **Settings → Administration Panel → Roles**. Super Admins have it by default.
285
237
 
286
- Sessions expire after 4 hours. The client should automatically reinitialize the connection.
238
+ ---
287
239
 
288
- ### Claude Desktop Not Connecting
240
+ ## Security
289
241
 
290
- 1. Check that `mcp-remote` is installed: `npx mcp-remote --version`
291
- 2. Verify your API token is valid in Strapi Admin
292
- 3. Check the Strapi logs for authentication errors
293
- 4. Ensure your Strapi instance is accessible from your machine
242
+ - Users can connect only with admin tokens they own, whether they pick one or a client is mapped to one. This is checked again when the code is exchanged.
243
+ - Clients without a secret must use PKCE (S256). Clients with a secret must send it.
244
+ - Self-registered clients may use only `https` redirect URIs, `http` on `localhost`, or native app schemes. Unused self-registered clients are removed after 30 days.
245
+ - Reusing a refresh token that was already replaced is rejected. If it happens more than 10 seconds after it was replaced, the plugin treats the token as leaked and ends the session.
246
+ - After 5 failed sign-in attempts, an IP address and email pair is locked for 15 minutes. The count is kept in memory on each server, capped at 10,000 entries.
247
+ - Strapi logs a warning if MCP OAuth is served over plain `http` on a host other than `localhost`. Use `https` in production.
248
+ - The consent page can't be framed and sends a strict Content Security Policy. The step between sign-in and approval uses a signed ticket that expires after 10 minutes and works only for that request.
294
249
 
295
- ### OAuth Flow Not Starting
250
+ ---
251
+
252
+ ## Troubleshooting
296
253
 
297
- 1. Verify the OAuth client is set to `active: true`
298
- 2. Check that the `strapiApiToken` in the OAuth client is valid
299
- 3. Look at Strapi logs for detailed error messages
254
+ | Problem | Fix |
255
+ |---|---|
256
+ | `404` on `/mcp` | Set `mcp: { enabled: true }` in `config/server.ts` and use Strapi 5.47 or later. |
257
+ | The client never opens a sign-in page | Run `curl -i -X POST https://your-strapi.com/mcp`. You should get `401` with a `WWW-Authenticate` header. If the header is missing, the plugin isn't enabled. |
258
+ | "You don't have any admin tokens to connect with" | Create one in **Settings → Admin Tokens**, then choose **Refresh**. If you can't, ask an admin to enable **Admin Tokens** for your role. |
259
+ | A token is missing from the consent page | Only your own, unexpired tokens are listed. Tokens created before `ENCRYPTION_KEY` was set can't be used; regenerate them. |
260
+ | "Only that token's owner can connect it" | The client is mapped to someone else's token. Ask that person to connect, or change the client to **Picked when connecting**. |
261
+ | "The admin token for … was deleted" | Choose a new token for the client on the **MCP OAuth** page. |
262
+ | Sign-in page links use `http://` or the wrong host | Set `url` in `config/server.ts`, or `proxy: true` behind a reverse proxy. |
263
+ | "The redirect URI is not registered for this client" | For clients added in the admin panel, the redirect URI must match exactly. `*` matches any run of characters except `/`. |
264
+ | A client suddenly gets `401` | Its session was revoked, its token was deleted or regenerated, or the user was deactivated. Reconnect. |
300
265
 
301
266
  ---
302
267
 
303
- ## Compatible MCP Plugins
268
+ ## Roadmap
304
269
 
305
- These plugins support `strapi-oauth-mcp-manager`:
270
+ - **Phase 2:** sign in on the consent page with admin SSO and social providers such as GitHub and Google.
306
271
 
307
- - [yt-transcript-strapi-plugin](https://www.npmjs.com/package/yt-transcript-strapi-plugin) - YouTube transcript tools
308
- - [strapi-content-mcp](https://www.npmjs.com/package/strapi-content-mcp) - Strapi content management tools
272
+ See [CHANGELOG.md](./CHANGELOG.md) for release notes.
309
273
 
310
274
  ---
311
275
 
312
- ## Requirements
276
+ ## Contributing
277
+
278
+ Issues and pull requests are welcome.
313
279
 
314
- - Strapi v5.x
315
- - Node.js >= 18
280
+ ```bash
281
+ npm install
282
+ npm test # unit tests for PKCE, consent tickets, redirect URI rules and client credentials
283
+ npm run build # Strapi loads the plugin from dist/, so rebuild after each change
284
+ ```
285
+
286
+ End-to-end tests run against a development Strapi app that has the MCP server enabled, this plugin installed, an admin user, and an `Article` collection type with `title` and `body` fields:
287
+
288
+ ```bash
289
+ BASE=http://localhost:1337 ADMIN_EMAIL=you@example.com ADMIN_PASSWORD=... npm run test:e2e
290
+ ```
291
+
292
+ The tests sign in many times, so turn off the admin login rate limit in that app (`rateLimit: { enabled: false }` in `config/admin.ts`). They also create users, tokens and sessions and change the Editor and Author roles temporarily, so don't point them at production.
293
+
294
+ ---
316
295
 
317
296
  ## License
318
297
 
319
- MIT
298
+ [MIT](./LICENSE)