strapi-oauth-mcp-manager 0.1.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/README.md +143 -0
- package/dist/_chunks/App-CjW3NftW.mjs +23 -0
- package/dist/_chunks/App-DsMhfKkM.js +23 -0
- package/dist/_chunks/en-B4KWt_jN.js +4 -0
- package/dist/_chunks/en-Byx4XI2L.mjs +4 -0
- package/dist/_chunks/index-B2ShbPnj.js +65 -0
- package/dist/_chunks/index-DzBaU9Fw.mjs +66 -0
- package/dist/admin/index.js +3 -0
- package/dist/admin/index.mjs +4 -0
- package/dist/admin/src/components/Initializer.d.ts +5 -0
- package/dist/admin/src/components/PluginIcon.d.ts +2 -0
- package/dist/admin/src/index.d.ts +10 -0
- package/dist/admin/src/pages/App.d.ts +2 -0
- package/dist/admin/src/pages/HomePage.d.ts +2 -0
- package/dist/admin/src/pluginId.d.ts +1 -0
- package/dist/admin/src/utils/getTranslation.d.ts +2 -0
- package/dist/server/index.js +800 -0
- package/dist/server/index.mjs +801 -0
- package/dist/server/src/bootstrap.d.ts +5 -0
- package/dist/server/src/config/index.d.ts +5 -0
- package/dist/server/src/content-types/index.d.ts +208 -0
- package/dist/server/src/controllers/index.d.ts +11 -0
- package/dist/server/src/controllers/oauth.d.ts +31 -0
- package/dist/server/src/destroy.d.ts +5 -0
- package/dist/server/src/index.d.ts +278 -0
- package/dist/server/src/middlewares/index.d.ts +6 -0
- package/dist/server/src/middlewares/mcp-oauth.d.ts +19 -0
- package/dist/server/src/pluginId.d.ts +1 -0
- package/dist/server/src/policies/index.d.ts +2 -0
- package/dist/server/src/register.d.ts +5 -0
- package/dist/server/src/routes/admin/index.d.ts +2 -0
- package/dist/server/src/routes/content-api/index.d.ts +10 -0
- package/dist/server/src/routes/index.d.ts +19 -0
- package/dist/server/src/services/endpoint.d.ts +37 -0
- package/dist/server/src/services/index.d.ts +22 -0
- package/dist/server/src/services/oauth.d.ts +32 -0
- package/package.json +88 -0
package/README.md
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Strapi OAuth MCP Manager
|
|
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.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
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
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install strapi-oauth-mcp-manager
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Configuration
|
|
22
|
+
|
|
23
|
+
Add the plugin to your `config/plugins.ts`:
|
|
24
|
+
|
|
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
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## How It Works
|
|
38
|
+
|
|
39
|
+
### For MCP Plugin Developers
|
|
40
|
+
|
|
41
|
+
MCP plugins register with the OAuth manager to gain authentication support:
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
// In your plugin's bootstrap.ts
|
|
45
|
+
const oauthPlugin = strapi.plugin('strapi-oauth-mcp-manager');
|
|
46
|
+
|
|
47
|
+
if (oauthPlugin) {
|
|
48
|
+
await oauthPlugin.service('endpoint').register({
|
|
49
|
+
name: 'My MCP Plugin',
|
|
50
|
+
pluginId: 'my-mcp-plugin',
|
|
51
|
+
path: '/api/my-mcp-plugin/mcp',
|
|
52
|
+
description: 'MCP endpoint for my tools',
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### For Users
|
|
58
|
+
|
|
59
|
+
1. **Create an OAuth Client** in Strapi Admin:
|
|
60
|
+
- Go to Content Manager > MCP OAuth Client
|
|
61
|
+
- Add client name, ID, secret, and redirect URIs
|
|
62
|
+
- Link to a Strapi API token
|
|
63
|
+
|
|
64
|
+
2. **Configure ChatGPT**:
|
|
65
|
+
- MCP Server URL: `https://your-domain/api/your-mcp-plugin/mcp`
|
|
66
|
+
- OAuth Client ID: From Strapi admin
|
|
67
|
+
- OAuth Client Secret: From Strapi admin
|
|
68
|
+
|
|
69
|
+
## OAuth Endpoints
|
|
70
|
+
|
|
71
|
+
| Endpoint | URL |
|
|
72
|
+
|----------|-----|
|
|
73
|
+
| Authorization | `/api/strapi-oauth-mcp-manager/oauth/authorize` |
|
|
74
|
+
| Token | `/api/strapi-oauth-mcp-manager/oauth/token` |
|
|
75
|
+
| Discovery (RFC 8414) | `/api/strapi-oauth-mcp-manager/.well-known/oauth-authorization-server` |
|
|
76
|
+
| Protected Resource (RFC 9728) | `/api/strapi-oauth-mcp-manager/.well-known/oauth-protected-resource` |
|
|
77
|
+
|
|
78
|
+
## Content Types
|
|
79
|
+
|
|
80
|
+
The plugin creates these content types:
|
|
81
|
+
|
|
82
|
+
| Content Type | Purpose |
|
|
83
|
+
|--------------|---------|
|
|
84
|
+
| `mcp-oauth-client` | OAuth client configurations |
|
|
85
|
+
| `mcp-oauth-code` | Authorization codes (temporary) |
|
|
86
|
+
| `mcp-oauth-token` | Access and refresh tokens |
|
|
87
|
+
| `mcp-endpoint` | Registered MCP endpoints |
|
|
88
|
+
|
|
89
|
+
## Creating an OAuth Client for ChatGPT
|
|
90
|
+
|
|
91
|
+
In Strapi Admin > Content Manager > MCP OAuth Client:
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"name": "ChatGPT",
|
|
96
|
+
"clientId": "chatgpt",
|
|
97
|
+
"clientSecret": "your-secure-secret",
|
|
98
|
+
"redirectUris": ["https://chatgpt.com/connector_platform_oauth_redirect"],
|
|
99
|
+
"strapiApiToken": "your-strapi-api-token",
|
|
100
|
+
"active": true
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Authentication Flow
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
1. Client hits MCP endpoint without auth
|
|
108
|
+
2. Returns 401 with WWW-Authenticate header
|
|
109
|
+
3. Client discovers OAuth endpoints via .well-known
|
|
110
|
+
4. Client completes OAuth authorization code flow
|
|
111
|
+
5. Client receives access token
|
|
112
|
+
6. Client makes MCP requests with Bearer token
|
|
113
|
+
7. OAuth manager validates and forwards to MCP plugin
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Claude Desktop (API Token)
|
|
117
|
+
|
|
118
|
+
For Claude Desktop, use direct Strapi API tokens:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"mcpServers": {
|
|
123
|
+
"your-mcp": {
|
|
124
|
+
"command": "npx",
|
|
125
|
+
"args": [
|
|
126
|
+
"mcp-remote",
|
|
127
|
+
"https://your-domain/api/your-mcp-plugin/mcp",
|
|
128
|
+
"--header",
|
|
129
|
+
"Authorization: Bearer YOUR_STRAPI_API_TOKEN"
|
|
130
|
+
]
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Requirements
|
|
137
|
+
|
|
138
|
+
- Strapi v5.x
|
|
139
|
+
- Node.js >= 18
|
|
140
|
+
|
|
141
|
+
## License
|
|
142
|
+
|
|
143
|
+
MIT
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Page } from "@strapi/strapi/admin";
|
|
3
|
+
import { Routes, Route } from "react-router-dom";
|
|
4
|
+
import { Main } from "@strapi/design-system";
|
|
5
|
+
import { useIntl } from "react-intl";
|
|
6
|
+
import { P as PLUGIN_ID } from "./index-DzBaU9Fw.mjs";
|
|
7
|
+
const getTranslation = (id) => `${PLUGIN_ID}.${id}`;
|
|
8
|
+
const HomePage = () => {
|
|
9
|
+
const { formatMessage } = useIntl();
|
|
10
|
+
return /* @__PURE__ */ jsx(Main, { children: /* @__PURE__ */ jsxs("h1", { children: [
|
|
11
|
+
"Welcome to ",
|
|
12
|
+
formatMessage({ id: getTranslation("plugin.name") })
|
|
13
|
+
] }) });
|
|
14
|
+
};
|
|
15
|
+
const App = () => {
|
|
16
|
+
return /* @__PURE__ */ jsxs(Routes, { children: [
|
|
17
|
+
/* @__PURE__ */ jsx(Route, { index: true, element: /* @__PURE__ */ jsx(HomePage, {}) }),
|
|
18
|
+
/* @__PURE__ */ jsx(Route, { path: "*", element: /* @__PURE__ */ jsx(Page.Error, {}) })
|
|
19
|
+
] });
|
|
20
|
+
};
|
|
21
|
+
export {
|
|
22
|
+
App
|
|
23
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const admin = require("@strapi/strapi/admin");
|
|
5
|
+
const reactRouterDom = require("react-router-dom");
|
|
6
|
+
const designSystem = require("@strapi/design-system");
|
|
7
|
+
const reactIntl = require("react-intl");
|
|
8
|
+
const index = require("./index-B2ShbPnj.js");
|
|
9
|
+
const getTranslation = (id) => `${index.PLUGIN_ID}.${id}`;
|
|
10
|
+
const HomePage = () => {
|
|
11
|
+
const { formatMessage } = reactIntl.useIntl();
|
|
12
|
+
return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Main, { children: /* @__PURE__ */ jsxRuntime.jsxs("h1", { children: [
|
|
13
|
+
"Welcome to ",
|
|
14
|
+
formatMessage({ id: getTranslation("plugin.name") })
|
|
15
|
+
] }) });
|
|
16
|
+
};
|
|
17
|
+
const App = () => {
|
|
18
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactRouterDom.Routes, { children: [
|
|
19
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { index: true, element: /* @__PURE__ */ jsxRuntime.jsx(HomePage, {}) }),
|
|
20
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "*", element: /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Error, {}) })
|
|
21
|
+
] });
|
|
22
|
+
};
|
|
23
|
+
exports.App = App;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const react = require("react");
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const icons = require("@strapi/icons");
|
|
5
|
+
const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
|
|
6
|
+
const v = glob[path];
|
|
7
|
+
if (v) {
|
|
8
|
+
return typeof v === "function" ? v() : Promise.resolve(v);
|
|
9
|
+
}
|
|
10
|
+
return new Promise((_, reject) => {
|
|
11
|
+
(typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
|
|
12
|
+
reject.bind(
|
|
13
|
+
null,
|
|
14
|
+
new Error(
|
|
15
|
+
"Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
|
|
16
|
+
)
|
|
17
|
+
)
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
const PLUGIN_ID = "strapi-oauth-mcp-manager";
|
|
22
|
+
const Initializer = ({ setPlugin }) => {
|
|
23
|
+
const ref = react.useRef(setPlugin);
|
|
24
|
+
react.useEffect(() => {
|
|
25
|
+
ref.current(PLUGIN_ID);
|
|
26
|
+
}, []);
|
|
27
|
+
return null;
|
|
28
|
+
};
|
|
29
|
+
const PluginIcon = () => /* @__PURE__ */ jsxRuntime.jsx(icons.PuzzlePiece, {});
|
|
30
|
+
const index = {
|
|
31
|
+
register(app) {
|
|
32
|
+
app.addMenuLink({
|
|
33
|
+
to: `plugins/${PLUGIN_ID}`,
|
|
34
|
+
icon: PluginIcon,
|
|
35
|
+
intlLabel: {
|
|
36
|
+
id: `${PLUGIN_ID}.plugin.name`,
|
|
37
|
+
defaultMessage: PLUGIN_ID
|
|
38
|
+
},
|
|
39
|
+
Component: async () => {
|
|
40
|
+
const { App } = await Promise.resolve().then(() => require("./App-DsMhfKkM.js"));
|
|
41
|
+
return App;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
app.registerPlugin({
|
|
45
|
+
id: PLUGIN_ID,
|
|
46
|
+
initializer: Initializer,
|
|
47
|
+
isReady: false,
|
|
48
|
+
name: PLUGIN_ID
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
async registerTrads({ locales }) {
|
|
52
|
+
return Promise.all(
|
|
53
|
+
locales.map(async (locale) => {
|
|
54
|
+
try {
|
|
55
|
+
const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => Promise.resolve().then(() => require("./en-B4KWt_jN.js")) }), `./translations/${locale}.json`, 3);
|
|
56
|
+
return { data, locale };
|
|
57
|
+
} catch {
|
|
58
|
+
return { data: {}, locale };
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
exports.PLUGIN_ID = PLUGIN_ID;
|
|
65
|
+
exports.index = index;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { useRef, useEffect } from "react";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { PuzzlePiece } from "@strapi/icons";
|
|
4
|
+
const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
|
|
5
|
+
const v = glob[path];
|
|
6
|
+
if (v) {
|
|
7
|
+
return typeof v === "function" ? v() : Promise.resolve(v);
|
|
8
|
+
}
|
|
9
|
+
return new Promise((_, reject) => {
|
|
10
|
+
(typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
|
|
11
|
+
reject.bind(
|
|
12
|
+
null,
|
|
13
|
+
new Error(
|
|
14
|
+
"Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
|
|
15
|
+
)
|
|
16
|
+
)
|
|
17
|
+
);
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
const PLUGIN_ID = "strapi-oauth-mcp-manager";
|
|
21
|
+
const Initializer = ({ setPlugin }) => {
|
|
22
|
+
const ref = useRef(setPlugin);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
ref.current(PLUGIN_ID);
|
|
25
|
+
}, []);
|
|
26
|
+
return null;
|
|
27
|
+
};
|
|
28
|
+
const PluginIcon = () => /* @__PURE__ */ jsx(PuzzlePiece, {});
|
|
29
|
+
const index = {
|
|
30
|
+
register(app) {
|
|
31
|
+
app.addMenuLink({
|
|
32
|
+
to: `plugins/${PLUGIN_ID}`,
|
|
33
|
+
icon: PluginIcon,
|
|
34
|
+
intlLabel: {
|
|
35
|
+
id: `${PLUGIN_ID}.plugin.name`,
|
|
36
|
+
defaultMessage: PLUGIN_ID
|
|
37
|
+
},
|
|
38
|
+
Component: async () => {
|
|
39
|
+
const { App } = await import("./App-CjW3NftW.mjs");
|
|
40
|
+
return App;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
app.registerPlugin({
|
|
44
|
+
id: PLUGIN_ID,
|
|
45
|
+
initializer: Initializer,
|
|
46
|
+
isReady: false,
|
|
47
|
+
name: PLUGIN_ID
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
async registerTrads({ locales }) {
|
|
51
|
+
return Promise.all(
|
|
52
|
+
locales.map(async (locale) => {
|
|
53
|
+
try {
|
|
54
|
+
const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => import("./en-Byx4XI2L.mjs") }), `./translations/${locale}.json`, 3);
|
|
55
|
+
return { data, locale };
|
|
56
|
+
} catch {
|
|
57
|
+
return { data: {}, locale };
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
export {
|
|
64
|
+
PLUGIN_ID as P,
|
|
65
|
+
index as i
|
|
66
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PLUGIN_ID = "strapi-oauth-mcp-manager";
|