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.
- package/CHANGELOG.md +33 -0
- package/LICENSE +21 -0
- package/README.md +211 -232
- package/dist/_chunks/App-Ddu3EpO8.mjs +397 -0
- package/dist/_chunks/App-WKeHpRl7.js +397 -0
- package/dist/_chunks/{en-B4KWt_jN.js → en-Dw8Cn8uZ.js} +3 -1
- package/dist/_chunks/en-UyIAE3Kz.mjs +6 -0
- package/dist/_chunks/{index-B2ShbPnj.js → index-BPOlIxN_.js} +4 -3
- package/dist/_chunks/{index-DzBaU9Fw.mjs → index-BrZfQfFg.mjs} +4 -3
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +1530 -432
- package/dist/server/index.mjs +1531 -433
- package/dist/server/src/bootstrap.d.ts +1 -0
- package/dist/server/src/config/index.d.ts +33 -2
- package/dist/server/src/content-types/index.d.ts +57 -10
- package/dist/server/src/controllers/admin.d.ts +16 -0
- package/dist/server/src/controllers/index.d.ts +17 -1
- package/dist/server/src/controllers/oauth.d.ts +21 -19
- package/dist/server/src/destroy.d.ts +1 -4
- package/dist/server/src/index.d.ts +176 -22
- package/dist/server/src/middlewares/index.d.ts +1 -1
- package/dist/server/src/middlewares/mcp-oauth.d.ts +10 -13
- package/dist/server/src/permissions.d.ts +3 -0
- package/dist/server/src/register.d.ts +1 -1
- package/dist/server/src/routes/admin/index.d.ts +13 -1
- package/dist/server/src/routes/index.d.ts +13 -1
- package/dist/server/src/services/index.d.ts +73 -3
- package/dist/server/src/services/oauth.d.ts +145 -13
- package/dist/server/src/utils/crypto.d.ts +20 -0
- package/dist/server/src/utils/url.d.ts +32 -0
- package/dist/server/src/views/authorize-page.d.ts +31 -0
- package/package.json +28 -15
- package/dist/_chunks/App-CjW3NftW.mjs +0 -23
- package/dist/_chunks/App-DsMhfKkM.js +0 -23
- package/dist/_chunks/en-Byx4XI2L.mjs +0 -4
- 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
|
-
|
|
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
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/strapi-oauth-mcp-manager) 
|
|
6
6
|
|
|
7
|
-
|
|
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
|
-
##
|
|
9
|
+
## Highlights
|
|
16
10
|
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
17
|
+
---
|
|
22
18
|
|
|
23
|
-
|
|
19
|
+
## Overview
|
|
24
20
|
|
|
25
|
-
|
|
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
|
-
|
|
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
|
-
|
|
27
|
+
---
|
|
45
28
|
|
|
46
|
-
|
|
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
|
-
|
|
31
|
+
1. Install the plugin in a Strapi 5.47+ project:
|
|
55
32
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
33
|
+
```bash
|
|
34
|
+
npm install strapi-oauth-mcp-manager
|
|
35
|
+
```
|
|
59
36
|
|
|
60
|
-
|
|
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
|
-
|
|
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
|
-
|
|
48
|
+
3. Enable the plugin in `config/plugins.ts`, then restart Strapi:
|
|
72
49
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
50
|
+
```typescript
|
|
51
|
+
export default () => ({
|
|
52
|
+
'strapi-oauth-mcp-manager': { enabled: true },
|
|
53
|
+
});
|
|
54
|
+
```
|
|
76
55
|
|
|
77
|
-
|
|
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
|
-
|
|
58
|
+
5. Connect a client. For Claude Code:
|
|
87
59
|
|
|
88
|
-
|
|
60
|
+
```bash
|
|
61
|
+
claude mcp add --transport http strapi http://localhost:1337/mcp
|
|
62
|
+
```
|
|
89
63
|
|
|
90
|
-
|
|
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
|
-
##
|
|
68
|
+
## Installation
|
|
95
69
|
|
|
96
|
-
|
|
70
|
+
**npm**
|
|
97
71
|
|
|
98
|
-
|
|
72
|
+
```bash
|
|
73
|
+
npm install strapi-oauth-mcp-manager
|
|
74
|
+
```
|
|
99
75
|
|
|
100
|
-
|
|
76
|
+
**yarn**
|
|
101
77
|
|
|
102
|
-
|
|
78
|
+
```bash
|
|
79
|
+
yarn add strapi-oauth-mcp-manager
|
|
80
|
+
```
|
|
103
81
|
|
|
104
|
-
|
|
82
|
+
Requirements:
|
|
105
83
|
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
92
|
+
## Access and permissions
|
|
139
93
|
|
|
140
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
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
|
-
|
|
109
|
+
A mapped client skips the picker. The consent page shows the mapped token, and every session uses it.
|
|
175
110
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
###
|
|
116
|
+
### Let users create tokens
|
|
191
117
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
-
##
|
|
126
|
+
## Revoking access
|
|
226
127
|
|
|
227
|
-
|
|
|
228
|
-
|
|
229
|
-
|
|
|
230
|
-
|
|
|
231
|
-
|
|
|
232
|
-
|
|
|
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
|
-
##
|
|
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
|
-
|
|
153
|
+
### Clients that need a client ID and secret
|
|
239
154
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
267
|
-
|--------|--------------|
|
|
268
|
-
| ChatGPT | `https://chatgpt.com/connector_platform_oauth_redirect` |
|
|
269
|
-
| Custom OAuth | Your app's callback URL |
|
|
202
|
+
### Endpoints
|
|
270
203
|
|
|
271
|
-
|
|
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
|
-
##
|
|
215
|
+
## Configuration
|
|
276
216
|
|
|
277
|
-
|
|
217
|
+
All options are optional. Defaults are shown.
|
|
278
218
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
-
|
|
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
|
-
|
|
238
|
+
---
|
|
287
239
|
|
|
288
|
-
|
|
240
|
+
## Security
|
|
289
241
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
-
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Troubleshooting
|
|
296
253
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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
|
-
##
|
|
268
|
+
## Roadmap
|
|
304
269
|
|
|
305
|
-
|
|
270
|
+
- **Phase 2:** sign in on the consent page with admin SSO and social providers such as GitHub and Google.
|
|
306
271
|
|
|
307
|
-
|
|
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
|
-
##
|
|
276
|
+
## Contributing
|
|
277
|
+
|
|
278
|
+
Issues and pull requests are welcome.
|
|
313
279
|
|
|
314
|
-
|
|
315
|
-
|
|
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)
|