@payloadcms/plugin-mcp 4.0.0-internal.a0ef1b8 → 4.0.0-internal.a2d053c

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 (259) hide show
  1. package/LICENSE.md +1 -1
  2. package/dist/defaultAccess.d.ts +3 -0
  3. package/dist/defaultAccess.d.ts.map +1 -0
  4. package/dist/defaultAccess.js +3 -0
  5. package/dist/defaultAccess.js.map +1 -0
  6. package/dist/defineTool.d.ts +8 -8
  7. package/dist/defineTool.d.ts.map +1 -1
  8. package/dist/endpoint/access.d.ts +12 -5
  9. package/dist/endpoint/access.d.ts.map +1 -1
  10. package/dist/endpoint/access.js +95 -88
  11. package/dist/endpoint/access.js.map +1 -1
  12. package/dist/endpoint/index.d.ts +1 -1
  13. package/dist/endpoint/index.d.ts.map +1 -1
  14. package/dist/endpoint/index.js +109 -16
  15. package/dist/endpoint/index.js.map +1 -1
  16. package/dist/endpoint/oauth.d.ts +39 -0
  17. package/dist/endpoint/oauth.d.ts.map +1 -0
  18. package/dist/endpoint/oauth.js +94 -0
  19. package/dist/endpoint/oauth.js.map +1 -0
  20. package/dist/endpoint/oauthLoginToken.d.ts +21 -0
  21. package/dist/endpoint/oauthLoginToken.d.ts.map +1 -0
  22. package/dist/endpoint/oauthLoginToken.js +79 -0
  23. package/dist/endpoint/oauthLoginToken.js.map +1 -0
  24. package/dist/endpoint/oauthServer.d.ts +12 -0
  25. package/dist/endpoint/oauthServer.d.ts.map +1 -0
  26. package/dist/endpoint/oauthServer.js +362 -0
  27. package/dist/endpoint/oauthServer.js.map +1 -0
  28. package/dist/exports/client.d.ts +1 -1
  29. package/dist/exports/client.d.ts.map +1 -1
  30. package/dist/exports/client.js +1 -1
  31. package/dist/exports/client.js.map +1 -1
  32. package/dist/exports/internal.d.ts +2 -0
  33. package/dist/exports/internal.d.ts.map +1 -0
  34. package/dist/exports/internal.js +3 -0
  35. package/dist/exports/internal.js.map +1 -0
  36. package/dist/index.d.ts +12 -4
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +30 -22
  39. package/dist/index.js.map +1 -1
  40. package/dist/mcp/buildMcpServer.d.ts +6 -6
  41. package/dist/mcp/buildMcpServer.d.ts.map +1 -1
  42. package/dist/mcp/buildMcpServer.js +146 -81
  43. package/dist/mcp/buildMcpServer.js.map +1 -1
  44. package/dist/mcp/builtin/collections/authTools.d.ts.map +1 -1
  45. package/dist/mcp/builtin/collections/authTools.js +123 -61
  46. package/dist/mcp/builtin/collections/authTools.js.map +1 -1
  47. package/dist/mcp/builtin/collections/countTool.d.ts +2 -0
  48. package/dist/mcp/builtin/collections/countTool.d.ts.map +1 -0
  49. package/dist/mcp/builtin/collections/countTool.js +63 -0
  50. package/dist/mcp/builtin/collections/countTool.js.map +1 -0
  51. package/dist/mcp/builtin/collections/countVersionsTool.d.ts +2 -0
  52. package/dist/mcp/builtin/collections/countVersionsTool.d.ts.map +1 -0
  53. package/dist/mcp/builtin/collections/countVersionsTool.js +60 -0
  54. package/dist/mcp/builtin/collections/countVersionsTool.js.map +1 -0
  55. package/dist/mcp/builtin/collections/createTool.d.ts +1 -1
  56. package/dist/mcp/builtin/collections/createTool.d.ts.map +1 -1
  57. package/dist/mcp/builtin/collections/createTool.js +114 -68
  58. package/dist/mcp/builtin/collections/createTool.js.map +1 -1
  59. package/dist/mcp/builtin/collections/deleteTool.d.ts +1 -1
  60. package/dist/mcp/builtin/collections/deleteTool.d.ts.map +1 -1
  61. package/dist/mcp/builtin/collections/deleteTool.js +55 -61
  62. package/dist/mcp/builtin/collections/deleteTool.js.map +1 -1
  63. package/dist/mcp/builtin/collections/duplicateTool.d.ts +2 -0
  64. package/dist/mcp/builtin/collections/duplicateTool.d.ts.map +1 -0
  65. package/dist/mcp/builtin/collections/duplicateTool.js +90 -0
  66. package/dist/mcp/builtin/collections/duplicateTool.js.map +1 -0
  67. package/dist/mcp/builtin/collections/fileInput.d.ts +28 -0
  68. package/dist/mcp/builtin/collections/fileInput.d.ts.map +1 -0
  69. package/dist/mcp/builtin/collections/fileInput.js +88 -0
  70. package/dist/mcp/builtin/collections/fileInput.js.map +1 -0
  71. package/dist/mcp/builtin/collections/findDistinctTool.d.ts +2 -0
  72. package/dist/mcp/builtin/collections/findDistinctTool.d.ts.map +1 -0
  73. package/dist/mcp/builtin/collections/findDistinctTool.js +77 -0
  74. package/dist/mcp/builtin/collections/findDistinctTool.js.map +1 -0
  75. package/dist/mcp/builtin/collections/findTool.d.ts +1 -1
  76. package/dist/mcp/builtin/collections/findTool.d.ts.map +1 -1
  77. package/dist/mcp/builtin/collections/findTool.js +62 -75
  78. package/dist/mcp/builtin/collections/findTool.js.map +1 -1
  79. package/dist/mcp/builtin/collections/findVersionByIDTool.d.ts +2 -0
  80. package/dist/mcp/builtin/collections/findVersionByIDTool.d.ts.map +1 -0
  81. package/dist/mcp/builtin/collections/findVersionByIDTool.js +74 -0
  82. package/dist/mcp/builtin/collections/findVersionByIDTool.js.map +1 -0
  83. package/dist/mcp/builtin/collections/findVersionsTool.d.ts +2 -0
  84. package/dist/mcp/builtin/collections/findVersionsTool.d.ts.map +1 -0
  85. package/dist/mcp/builtin/collections/findVersionsTool.js +84 -0
  86. package/dist/mcp/builtin/collections/findVersionsTool.js.map +1 -0
  87. package/dist/mcp/builtin/collections/getCollectionSchemaTool.d.ts +2 -0
  88. package/dist/mcp/builtin/collections/getCollectionSchemaTool.d.ts.map +1 -0
  89. package/dist/mcp/builtin/collections/getCollectionSchemaTool.js +103 -0
  90. package/dist/mcp/builtin/collections/getCollectionSchemaTool.js.map +1 -0
  91. package/dist/mcp/builtin/collections/restoreVersionTool.d.ts +2 -0
  92. package/dist/mcp/builtin/collections/restoreVersionTool.d.ts.map +1 -0
  93. package/dist/mcp/builtin/collections/restoreVersionTool.js +69 -0
  94. package/dist/mcp/builtin/collections/restoreVersionTool.js.map +1 -0
  95. package/dist/mcp/builtin/collections/updateTool.d.ts +1 -1
  96. package/dist/mcp/builtin/collections/updateTool.d.ts.map +1 -1
  97. package/dist/mcp/builtin/collections/updateTool.js +118 -137
  98. package/dist/mcp/builtin/collections/updateTool.js.map +1 -1
  99. package/dist/mcp/builtin/collections/uploadInstructionsTool.d.ts +2 -0
  100. package/dist/mcp/builtin/collections/uploadInstructionsTool.d.ts.map +1 -0
  101. package/dist/mcp/builtin/collections/uploadInstructionsTool.js +55 -0
  102. package/dist/mcp/builtin/collections/uploadInstructionsTool.js.map +1 -0
  103. package/dist/mcp/builtin/formatEntityError.d.ts +16 -0
  104. package/dist/mcp/builtin/formatEntityError.d.ts.map +1 -0
  105. package/dist/mcp/builtin/formatEntityError.js +55 -0
  106. package/dist/mcp/builtin/formatEntityError.js.map +1 -0
  107. package/dist/mcp/builtin/getConfigInfoTool.d.ts +2 -0
  108. package/dist/mcp/builtin/getConfigInfoTool.d.ts.map +1 -0
  109. package/dist/mcp/builtin/getConfigInfoTool.js +69 -0
  110. package/dist/mcp/builtin/getConfigInfoTool.js.map +1 -0
  111. package/dist/mcp/builtin/globals/countVersionsTool.d.ts +2 -0
  112. package/dist/mcp/builtin/globals/countVersionsTool.d.ts.map +1 -0
  113. package/dist/mcp/builtin/globals/countVersionsTool.js +60 -0
  114. package/dist/mcp/builtin/globals/countVersionsTool.js.map +1 -0
  115. package/dist/mcp/builtin/globals/findTool.d.ts.map +1 -1
  116. package/dist/mcp/builtin/globals/findTool.js +27 -36
  117. package/dist/mcp/builtin/globals/findTool.js.map +1 -1
  118. package/dist/mcp/builtin/globals/findVersionByIDTool.d.ts +2 -0
  119. package/dist/mcp/builtin/globals/findVersionByIDTool.d.ts.map +1 -0
  120. package/dist/mcp/builtin/globals/findVersionByIDTool.js +68 -0
  121. package/dist/mcp/builtin/globals/findVersionByIDTool.js.map +1 -0
  122. package/dist/mcp/builtin/globals/findVersionsTool.d.ts +2 -0
  123. package/dist/mcp/builtin/globals/findVersionsTool.d.ts.map +1 -0
  124. package/dist/mcp/builtin/globals/findVersionsTool.js +78 -0
  125. package/dist/mcp/builtin/globals/findVersionsTool.js.map +1 -0
  126. package/dist/mcp/builtin/globals/getGlobalSchemaTool.d.ts +2 -0
  127. package/dist/mcp/builtin/globals/getGlobalSchemaTool.d.ts.map +1 -0
  128. package/dist/mcp/builtin/globals/getGlobalSchemaTool.js +81 -0
  129. package/dist/mcp/builtin/globals/getGlobalSchemaTool.js.map +1 -0
  130. package/dist/mcp/builtin/globals/restoreVersionTool.d.ts +2 -0
  131. package/dist/mcp/builtin/globals/restoreVersionTool.d.ts.map +1 -0
  132. package/dist/mcp/builtin/globals/restoreVersionTool.js +70 -0
  133. package/dist/mcp/builtin/globals/restoreVersionTool.js.map +1 -0
  134. package/dist/mcp/builtin/globals/updateTool.d.ts.map +1 -1
  135. package/dist/mcp/builtin/globals/updateTool.js +44 -62
  136. package/dist/mcp/builtin/globals/updateTool.js.map +1 -1
  137. package/dist/mcp/builtinTools.d.ts +150 -19
  138. package/dist/mcp/builtinTools.d.ts.map +1 -1
  139. package/dist/mcp/builtinTools.js +125 -20
  140. package/dist/mcp/builtinTools.js.map +1 -1
  141. package/dist/mcp/sanitizeMCPConfig.d.ts +0 -1
  142. package/dist/mcp/sanitizeMCPConfig.d.ts.map +1 -1
  143. package/dist/mcp/sanitizeMCPConfig.js +127 -58
  144. package/dist/mcp/sanitizeMCPConfig.js.map +1 -1
  145. package/dist/stdio.d.ts +3 -3
  146. package/dist/stdio.d.ts.map +1 -1
  147. package/dist/stdio.js +86 -47
  148. package/dist/stdio.js.map +1 -1
  149. package/dist/types.d.ts +205 -114
  150. package/dist/types.d.ts.map +1 -1
  151. package/dist/types.js +2 -4
  152. package/dist/types.js.map +1 -1
  153. package/dist/utils/camelCase.d.ts.map +1 -1
  154. package/dist/utils/getPluginConfig.d.ts +1 -1
  155. package/dist/utils/getPluginConfig.js +1 -1
  156. package/dist/utils/getPluginConfig.js.map +1 -1
  157. package/dist/utils/resolveProjectRoot.d.ts.map +1 -1
  158. package/dist/utils/seal.d.ts +18 -0
  159. package/dist/utils/seal.d.ts.map +1 -0
  160. package/dist/utils/seal.js +27 -0
  161. package/dist/utils/seal.js.map +1 -0
  162. package/dist/utils/toStandardSchema.d.ts.map +1 -1
  163. package/dist/utils/toStandardSchema.js.map +1 -1
  164. package/package.json +27 -37
  165. package/src/defaultAccess.ts +3 -0
  166. package/src/endpoint/access.ts +99 -101
  167. package/src/endpoint/index.ts +95 -15
  168. package/src/endpoint/oauth.ts +92 -0
  169. package/src/endpoint/oauthLoginToken.ts +91 -0
  170. package/src/endpoint/oauthServer.ts +396 -0
  171. package/src/exports/client.ts +2 -1
  172. package/src/exports/internal.ts +1 -0
  173. package/src/index.ts +52 -25
  174. package/src/mcp/buildMcpServer.ts +181 -116
  175. package/src/mcp/builtin/collections/authTools.ts +124 -77
  176. package/src/mcp/builtin/collections/countTool.ts +61 -0
  177. package/src/mcp/builtin/collections/countVersionsTool.ts +60 -0
  178. package/src/mcp/builtin/collections/createTool.ts +115 -88
  179. package/src/mcp/builtin/collections/deleteTool.ts +49 -75
  180. package/src/mcp/builtin/collections/duplicateTool.ts +84 -0
  181. package/src/mcp/builtin/collections/fileInput.spec.ts +93 -0
  182. package/src/mcp/builtin/collections/fileInput.ts +127 -0
  183. package/src/mcp/builtin/collections/findDistinctTool.ts +67 -0
  184. package/src/mcp/builtin/collections/findTool.ts +58 -111
  185. package/src/mcp/builtin/collections/findVersionByIDTool.ts +66 -0
  186. package/src/mcp/builtin/collections/findVersionsTool.ts +83 -0
  187. package/src/mcp/builtin/collections/getCollectionSchemaTool.ts +91 -0
  188. package/src/mcp/builtin/collections/restoreVersionTool.ts +65 -0
  189. package/src/mcp/builtin/collections/updateTool.ts +106 -143
  190. package/src/mcp/builtin/collections/uploadInstructionsTool.ts +64 -0
  191. package/src/mcp/builtin/formatEntityError.ts +77 -0
  192. package/src/mcp/builtin/getConfigInfoTool.ts +68 -0
  193. package/src/mcp/builtin/globals/countVersionsTool.ts +60 -0
  194. package/src/mcp/builtin/globals/findTool.ts +28 -58
  195. package/src/mcp/builtin/globals/findVersionByIDTool.ts +64 -0
  196. package/src/mcp/builtin/globals/findVersionsTool.ts +69 -0
  197. package/src/mcp/builtin/globals/getGlobalSchemaTool.ts +73 -0
  198. package/src/mcp/builtin/globals/restoreVersionTool.ts +68 -0
  199. package/src/mcp/builtin/globals/updateTool.ts +52 -80
  200. package/src/mcp/builtinTools.ts +118 -41
  201. package/src/mcp/sanitizeMCPConfig.ts +121 -78
  202. package/src/stdio.ts +80 -47
  203. package/src/types.ts +223 -119
  204. package/src/utils/getPluginConfig.ts +1 -1
  205. package/src/utils/seal.ts +46 -0
  206. package/src/utils/toStandardSchema.ts +1 -1
  207. package/dist/@types/assets.d.js +0 -2
  208. package/dist/@types/assets.d.js.map +0 -1
  209. package/dist/collection/getAccessField.d.ts +0 -12
  210. package/dist/collection/getAccessField.d.ts.map +0 -1
  211. package/dist/collection/getAccessField.js +0 -57
  212. package/dist/collection/getAccessField.js.map +0 -1
  213. package/dist/collection/index.d.ts +0 -6
  214. package/dist/collection/index.d.ts.map +0 -1
  215. package/dist/collection/index.js +0 -60
  216. package/dist/collection/index.js.map +0 -1
  217. package/dist/components/AccessField/index.client.d.ts +0 -10
  218. package/dist/components/AccessField/index.client.d.ts.map +0 -1
  219. package/dist/components/AccessField/index.client.js +0 -305
  220. package/dist/components/AccessField/index.client.js.map +0 -1
  221. package/dist/components/AccessField/index.css +0 -93
  222. package/dist/utils/getVirtualFieldNames.d.ts +0 -14
  223. package/dist/utils/getVirtualFieldNames.d.ts.map +0 -1
  224. package/dist/utils/getVirtualFieldNames.js +0 -35
  225. package/dist/utils/getVirtualFieldNames.js.map +0 -1
  226. package/dist/utils/localAPIDefaults.d.ts +0 -20
  227. package/dist/utils/localAPIDefaults.d.ts.map +0 -1
  228. package/dist/utils/localAPIDefaults.js +0 -19
  229. package/dist/utils/localAPIDefaults.js.map +0 -1
  230. package/dist/utils/schemaConversion/buildToolInput.d.ts +0 -29
  231. package/dist/utils/schemaConversion/buildToolInput.d.ts.map +0 -1
  232. package/dist/utils/schemaConversion/buildToolInput.js +0 -51
  233. package/dist/utils/schemaConversion/buildToolInput.js.map +0 -1
  234. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.d.ts +0 -7
  235. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.d.ts.map +0 -1
  236. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.js +0 -20
  237. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.js.map +0 -1
  238. package/dist/utils/schemaConversion/sanitizeEntitySchema.d.ts +0 -15
  239. package/dist/utils/schemaConversion/sanitizeEntitySchema.d.ts.map +0 -1
  240. package/dist/utils/schemaConversion/sanitizeEntitySchema.js +0 -464
  241. package/dist/utils/schemaConversion/sanitizeEntitySchema.js.map +0 -1
  242. package/dist/utils/schemaConversion/sanitizeEntitySchema.spec.js +0 -158
  243. package/dist/utils/schemaConversion/sanitizeEntitySchema.spec.js.map +0 -1
  244. package/dist/utils/transformPointDataToPayload.d.ts +0 -7
  245. package/dist/utils/transformPointDataToPayload.d.ts.map +0 -1
  246. package/dist/utils/transformPointDataToPayload.js +0 -28
  247. package/dist/utils/transformPointDataToPayload.js.map +0 -1
  248. package/src/@types/assets.d.ts +0 -3
  249. package/src/collection/getAccessField.ts +0 -64
  250. package/src/collection/index.ts +0 -64
  251. package/src/components/AccessField/index.client.tsx +0 -344
  252. package/src/components/AccessField/index.css +0 -93
  253. package/src/utils/getVirtualFieldNames.ts +0 -53
  254. package/src/utils/localAPIDefaults.ts +0 -22
  255. package/src/utils/schemaConversion/buildToolInput.ts +0 -68
  256. package/src/utils/schemaConversion/removeVirtualFieldsFromSchema.ts +0 -27
  257. package/src/utils/schemaConversion/sanitizeEntitySchema.spec.ts +0 -103
  258. package/src/utils/schemaConversion/sanitizeEntitySchema.ts +0 -529
  259. package/src/utils/transformPointDataToPayload.ts +0 -40
@@ -0,0 +1,94 @@
1
+ import { bearerAuthChallengeResponse, OAuthError, OAuthErrorCode } from '@modelcontextprotocol/server';
2
+ import { createPayloadRequest, getPayload } from 'payload';
3
+ import { getPluginConfig } from '../utils/getPluginConfig.js';
4
+ /**
5
+ * Serves the OAuth protected resource metadata (RFC 9728) at `/api/mcp/oauth-protected-resource`.
6
+ * The OAuth challenge links here, so MCP clients find the authorization server without setup.
7
+ */ export const oauthProtectedResourceEndpoint = (req)=>getMetadataResponse({
8
+ req
9
+ });
10
+ /**
11
+ * Serves the same metadata for clients that ignore the challenge's link and only look at
12
+ * `/.well-known/oauth-protected-resource`. Mount it at the site root, e.g. in Next.js at
13
+ * `app/.well-known/oauth-protected-resource/[[...path]]/route.ts`:
14
+ *
15
+ * ```ts
16
+ * import config from '@payload-config'
17
+ * import { oauthProtectedResourceHandler } from '@payloadcms/plugin-mcp'
18
+ *
19
+ * export const GET = oauthProtectedResourceHandler(config)
20
+ * ```
21
+ */ export const oauthProtectedResourceHandler = (config)=>async (request)=>getMetadataResponse({
22
+ req: await createPayloadRequest({
23
+ payload: await getPayload({
24
+ config
25
+ }),
26
+ req: request
27
+ })
28
+ });
29
+ /**
30
+ * Answers an unauthenticated MCP request with a challenge linking to the protected resource
31
+ * metadata, which starts the client's OAuth flow.
32
+ */ export const oauthChallenge = ({ req })=>{
33
+ const { oauth } = getPluginConfig({
34
+ config: req.payload.config
35
+ });
36
+ const response = bearerAuthChallengeResponse(new OAuthError(OAuthErrorCode.InvalidToken, req.t('error:unauthorized')), {
37
+ requiredScopes: oauth?.type === 'external' ? oauth.scopes : undefined,
38
+ resourceMetadataUrl: `${getMCPURL({
39
+ config: req.payload.config,
40
+ url: req.url
41
+ })}/oauth-protected-resource`
42
+ });
43
+ // Lets browser-based MCP clients read the challenge, when Payload's `cors` setting allows them
44
+ response.headers.set('Access-Control-Expose-Headers', 'WWW-Authenticate');
45
+ return response;
46
+ };
47
+ /**
48
+ * The MCP endpoint's address, as advertised in the metadata's `resource`. Clients send it when
49
+ * they sign in, and authorization servers that support it put it in the access token's audience.
50
+ */ export const getMCPResource = ({ config, url })=>getPluginConfig({
51
+ config
52
+ }).oauth?.resource ?? getMCPURL({
53
+ config,
54
+ url
55
+ });
56
+ export const getMCPURL = ({ config, url })=>new URL(`${config.routes.api}/mcp`, config.serverURL || url).href;
57
+ const getMetadataResponse = async ({ req })=>{
58
+ const { config } = req.payload;
59
+ const { oauth } = getPluginConfig({
60
+ config
61
+ });
62
+ if (!oauth) {
63
+ return new Response(null, {
64
+ status: 404
65
+ });
66
+ }
67
+ return Response.json({
68
+ authorization_servers: oauth.type === 'builtin' ? [
69
+ `${getMCPURL({
70
+ config,
71
+ url: req.url
72
+ })}/oauth`
73
+ ] : typeof oauth.authorizationServers === 'function' ? await oauth.authorizationServers({
74
+ req
75
+ }) : oauth.authorizationServers,
76
+ bearer_methods_supported: [
77
+ 'header'
78
+ ],
79
+ resource: getMCPResource({
80
+ config,
81
+ url: req.url
82
+ }),
83
+ scopes_supported: oauth.type === 'external' ? oauth.scopes : undefined
84
+ }, // Public document, readable by browser-based MCP clients on any origin. Not cached, since
85
+ // without `serverURL` its URLs come from the request.
86
+ {
87
+ headers: {
88
+ 'Access-Control-Allow-Origin': '*',
89
+ 'Cache-Control': 'no-store'
90
+ }
91
+ });
92
+ };
93
+
94
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/endpoint/oauth.ts"],"sourcesContent":["import type { OAuthProtectedResourceMetadata } from '@modelcontextprotocol/server'\nimport type { PayloadHandler, PayloadRequest, SanitizedConfig } from 'payload'\n\nimport {\n bearerAuthChallengeResponse,\n OAuthError,\n OAuthErrorCode,\n} from '@modelcontextprotocol/server'\nimport { createPayloadRequest, getPayload } from 'payload'\n\nimport { getPluginConfig } from '../utils/getPluginConfig.js'\n\n/**\n * Serves the OAuth protected resource metadata (RFC 9728) at `/api/mcp/oauth-protected-resource`.\n * The OAuth challenge links here, so MCP clients find the authorization server without setup.\n */\nexport const oauthProtectedResourceEndpoint: PayloadHandler = (req) => getMetadataResponse({ req })\n\n/**\n * Serves the same metadata for clients that ignore the challenge's link and only look at\n * `/.well-known/oauth-protected-resource`. Mount it at the site root, e.g. in Next.js at\n * `app/.well-known/oauth-protected-resource/[[...path]]/route.ts`:\n *\n * ```ts\n * import config from '@payload-config'\n * import { oauthProtectedResourceHandler } from '@payloadcms/plugin-mcp'\n *\n * export const GET = oauthProtectedResourceHandler(config)\n * ```\n */\nexport const oauthProtectedResourceHandler =\n (config: Promise<SanitizedConfig> | SanitizedConfig) =>\n async (request: Request): Promise<Response> =>\n getMetadataResponse({\n req: await createPayloadRequest({ payload: await getPayload({ config }), req: request }),\n })\n\n/**\n * Answers an unauthenticated MCP request with a challenge linking to the protected resource\n * metadata, which starts the client's OAuth flow.\n */\nexport const oauthChallenge = ({ req }: { req: PayloadRequest }): Response => {\n const { oauth } = getPluginConfig({ config: req.payload.config })\n const response = bearerAuthChallengeResponse(\n new OAuthError(OAuthErrorCode.InvalidToken, req.t('error:unauthorized')),\n {\n requiredScopes: oauth?.type === 'external' ? oauth.scopes : undefined,\n resourceMetadataUrl: `${getMCPURL({ config: req.payload.config, url: req.url! })}/oauth-protected-resource`,\n },\n )\n\n // Lets browser-based MCP clients read the challenge, when Payload's `cors` setting allows them\n response.headers.set('Access-Control-Expose-Headers', 'WWW-Authenticate')\n\n return response\n}\n\n/**\n * The MCP endpoint's address, as advertised in the metadata's `resource`. Clients send it when\n * they sign in, and authorization servers that support it put it in the access token's audience.\n */\nexport const getMCPResource = ({ config, url }: { config: SanitizedConfig; url: string }): string =>\n getPluginConfig({ config }).oauth?.resource ?? getMCPURL({ config, url })\n\nexport const getMCPURL = ({ config, url }: { config: SanitizedConfig; url: string }): string =>\n new URL(`${config.routes.api}/mcp`, config.serverURL || url).href\n\nconst getMetadataResponse = async ({ req }: { req: PayloadRequest }): Promise<Response> => {\n const { config } = req.payload\n const { oauth } = getPluginConfig({ config })\n\n if (!oauth) {\n return new Response(null, { status: 404 })\n }\n\n return Response.json(\n {\n authorization_servers:\n oauth.type === 'builtin'\n ? [`${getMCPURL({ config, url: req.url! })}/oauth`]\n : typeof oauth.authorizationServers === 'function'\n ? await oauth.authorizationServers({ req })\n : oauth.authorizationServers,\n bearer_methods_supported: ['header'],\n resource: getMCPResource({ config, url: req.url! }),\n scopes_supported: oauth.type === 'external' ? oauth.scopes : undefined,\n } satisfies OAuthProtectedResourceMetadata,\n // Public document, readable by browser-based MCP clients on any origin. Not cached, since\n // without `serverURL` its URLs come from the request.\n { headers: { 'Access-Control-Allow-Origin': '*', 'Cache-Control': 'no-store' } },\n )\n}\n"],"names":["bearerAuthChallengeResponse","OAuthError","OAuthErrorCode","createPayloadRequest","getPayload","getPluginConfig","oauthProtectedResourceEndpoint","req","getMetadataResponse","oauthProtectedResourceHandler","config","request","payload","oauthChallenge","oauth","response","InvalidToken","t","requiredScopes","type","scopes","undefined","resourceMetadataUrl","getMCPURL","url","headers","set","getMCPResource","resource","URL","routes","api","serverURL","href","Response","status","json","authorization_servers","authorizationServers","bearer_methods_supported","scopes_supported"],"mappings":"AAGA,SACEA,2BAA2B,EAC3BC,UAAU,EACVC,cAAc,QACT,+BAA8B;AACrC,SAASC,oBAAoB,EAAEC,UAAU,QAAQ,UAAS;AAE1D,SAASC,eAAe,QAAQ,8BAA6B;AAE7D;;;CAGC,GACD,OAAO,MAAMC,iCAAiD,CAACC,MAAQC,oBAAoB;QAAED;IAAI,GAAE;AAEnG;;;;;;;;;;;CAWC,GACD,OAAO,MAAME,gCACX,CAACC,SACD,OAAOC,UACLH,oBAAoB;YAClBD,KAAK,MAAMJ,qBAAqB;gBAAES,SAAS,MAAMR,WAAW;oBAAEM;gBAAO;gBAAIH,KAAKI;YAAQ;QACxF,GAAE;AAEN;;;CAGC,GACD,OAAO,MAAME,iBAAiB,CAAC,EAAEN,GAAG,EAA2B;IAC7D,MAAM,EAAEO,KAAK,EAAE,GAAGT,gBAAgB;QAAEK,QAAQH,IAAIK,OAAO,CAACF,MAAM;IAAC;IAC/D,MAAMK,WAAWf,4BACf,IAAIC,WAAWC,eAAec,YAAY,EAAET,IAAIU,CAAC,CAAC,wBAClD;QACEC,gBAAgBJ,OAAOK,SAAS,aAAaL,MAAMM,MAAM,GAAGC;QAC5DC,qBAAqB,GAAGC,UAAU;YAAEb,QAAQH,IAAIK,OAAO,CAACF,MAAM;YAAEc,KAAKjB,IAAIiB,GAAG;QAAE,GAAG,yBAAyB,CAAC;IAC7G;IAGF,+FAA+F;IAC/FT,SAASU,OAAO,CAACC,GAAG,CAAC,iCAAiC;IAEtD,OAAOX;AACT,EAAC;AAED;;;CAGC,GACD,OAAO,MAAMY,iBAAiB,CAAC,EAAEjB,MAAM,EAAEc,GAAG,EAA4C,GACtFnB,gBAAgB;QAAEK;IAAO,GAAGI,KAAK,EAAEc,YAAYL,UAAU;QAAEb;QAAQc;IAAI,GAAE;AAE3E,OAAO,MAAMD,YAAY,CAAC,EAAEb,MAAM,EAAEc,GAAG,EAA4C,GACjF,IAAIK,IAAI,GAAGnB,OAAOoB,MAAM,CAACC,GAAG,CAAC,IAAI,CAAC,EAAErB,OAAOsB,SAAS,IAAIR,KAAKS,IAAI,CAAA;AAEnE,MAAMzB,sBAAsB,OAAO,EAAED,GAAG,EAA2B;IACjE,MAAM,EAAEG,MAAM,EAAE,GAAGH,IAAIK,OAAO;IAC9B,MAAM,EAAEE,KAAK,EAAE,GAAGT,gBAAgB;QAAEK;IAAO;IAE3C,IAAI,CAACI,OAAO;QACV,OAAO,IAAIoB,SAAS,MAAM;YAAEC,QAAQ;QAAI;IAC1C;IAEA,OAAOD,SAASE,IAAI,CAClB;QACEC,uBACEvB,MAAMK,IAAI,KAAK,YACX;YAAC,GAAGI,UAAU;gBAAEb;gBAAQc,KAAKjB,IAAIiB,GAAG;YAAE,GAAG,MAAM,CAAC;SAAC,GACjD,OAAOV,MAAMwB,oBAAoB,KAAK,aACpC,MAAMxB,MAAMwB,oBAAoB,CAAC;YAAE/B;QAAI,KACvCO,MAAMwB,oBAAoB;QAClCC,0BAA0B;YAAC;SAAS;QACpCX,UAAUD,eAAe;YAAEjB;YAAQc,KAAKjB,IAAIiB,GAAG;QAAE;QACjDgB,kBAAkB1B,MAAMK,IAAI,KAAK,aAAaL,MAAMM,MAAM,GAAGC;IAC/D,GACA,0FAA0F;IAC1F,sDAAsD;IACtD;QAAEI,SAAS;YAAE,+BAA+B;YAAK,iBAAiB;QAAW;IAAE;AAEnF"}
@@ -0,0 +1,21 @@
1
+ import type { PayloadRequest } from 'payload';
2
+ import type { MCPOAuthTokens } from '../types.js';
3
+ /**
4
+ * The default `issueToken`: a Payload login token, sealed so it only works for MCP, where
5
+ * `authenticateLoginToken` unseals it. Otherwise an MCP client could use it as the user's admin
6
+ * panel login. When the collection uses sessions, the client also gets a refresh token.
7
+ */
8
+ export declare const issueLoginToken: ({ req, }: {
9
+ req: PayloadRequest;
10
+ }) => Promise<MCPOAuthTokens>;
11
+ /** The default `refreshToken`. Payload logs out the session when a refresh token is reused. */
12
+ export declare const refreshLoginToken: ({ refreshToken, req, }: {
13
+ refreshToken: string;
14
+ req: PayloadRequest;
15
+ }) => Promise<MCPOAuthTokens | null>;
16
+ /** Finds the user of an MCP request's access token from `issueLoginToken`. */
17
+ export declare const authenticateLoginToken: ({ req, token, }: {
18
+ req: PayloadRequest;
19
+ token: string;
20
+ }) => Promise<PayloadRequest['user']>;
21
+ //# sourceMappingURL=oauthLoginToken.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauthLoginToken.d.ts","sourceRoot":"","sources":["../../src/endpoint/oauthLoginToken.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAI7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAIjD;;;;GAIG;AACH,eAAO,MAAM,eAAe,aAEzB;IACD,GAAG,EAAE,cAAc,CAAA;CACpB,KAAG,OAAO,CAAC,cAAc,CA6BzB,CAAA;AAED,+FAA+F;AAC/F,eAAO,MAAM,iBAAiB,2BAG3B;IACD,YAAY,EAAE,MAAM,CAAA;IACpB,GAAG,EAAE,cAAc,CAAA;CACpB,KAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAUhC,CAAA;AAED,8EAA8E;AAC9E,eAAO,MAAM,sBAAsB,oBAGhC;IACD,GAAG,EAAE,cAAc,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;CACd,KAAG,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,CAajC,CAAA"}
@@ -0,0 +1,79 @@
1
+ import { APIError, createAuthToken, executeAuthStrategies, redeemRefreshToken } from 'payload';
2
+ import { seal, unseal } from '../utils/seal.js';
3
+ /**
4
+ * The default `issueToken`: a Payload login token, sealed so it only works for MCP, where
5
+ * `authenticateLoginToken` unseals it. Otherwise an MCP client could use it as the user's admin
6
+ * panel login. When the collection uses sessions, the client also gets a refresh token.
7
+ */ export const issueLoginToken = async ({ req })=>{
8
+ const collectionConfig = req.payload.collections[req.user.collection].config;
9
+ if (collectionConfig.auth.disableLocalStrategy) {
10
+ throw new APIError(`MCP clients can't get Payload login tokens for the "${collectionConfig.slug}" collection because its local strategy is disabled. Pass oauth.issueToken to the MCP plugin.`);
11
+ }
12
+ // The full document, like the one a password login starts its session on
13
+ const user = await req.payload.db.findOne({
14
+ collection: collectionConfig.slug,
15
+ req,
16
+ where: {
17
+ id: {
18
+ equals: req.user.id
19
+ }
20
+ }
21
+ });
22
+ const { refreshToken, token } = await createAuthToken({
23
+ collectionConfig,
24
+ email: user.email,
25
+ req,
26
+ shouldCreateRefreshToken: true,
27
+ user
28
+ });
29
+ return {
30
+ accessToken: sealLoginToken({
31
+ req,
32
+ token
33
+ }),
34
+ expiresIn: collectionConfig.auth.tokenExpiration,
35
+ refreshToken
36
+ };
37
+ };
38
+ /** The default `refreshToken`. Payload logs out the session when a refresh token is reused. */ export const refreshLoginToken = async ({ refreshToken, req })=>{
39
+ const refreshed = await redeemRefreshToken({
40
+ refreshToken,
41
+ req
42
+ });
43
+ return refreshed && {
44
+ accessToken: sealLoginToken({
45
+ req,
46
+ token: refreshed.token
47
+ }),
48
+ expiresIn: refreshed.collectionConfig.auth.tokenExpiration,
49
+ refreshToken: refreshed.refreshToken
50
+ };
51
+ };
52
+ /** Finds the user of an MCP request's access token from `issueLoginToken`. */ export const authenticateLoginToken = async ({ req, token })=>{
53
+ const loginToken = unseal({
54
+ type: 'access',
55
+ req,
56
+ value: token
57
+ });
58
+ if (!loginToken) {
59
+ return null;
60
+ }
61
+ const headers = new Headers({
62
+ Authorization: `JWT ${loginToken.token}`,
63
+ DisableAutologin: 'true'
64
+ });
65
+ return (await executeAuthStrategies({
66
+ headers,
67
+ payload: req.payload,
68
+ req
69
+ })).user;
70
+ };
71
+ const sealLoginToken = ({ req, token })=>seal({
72
+ type: 'access',
73
+ data: {
74
+ token
75
+ },
76
+ req
77
+ });
78
+
79
+ //# sourceMappingURL=oauthLoginToken.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/endpoint/oauthLoginToken.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\nimport { APIError, createAuthToken, executeAuthStrategies, redeemRefreshToken } from 'payload'\n\nimport type { MCPOAuthTokens } from '../types.js'\n\nimport { seal, unseal } from '../utils/seal.js'\n\n/**\n * The default `issueToken`: a Payload login token, sealed so it only works for MCP, where\n * `authenticateLoginToken` unseals it. Otherwise an MCP client could use it as the user's admin\n * panel login. When the collection uses sessions, the client also gets a refresh token.\n */\nexport const issueLoginToken = async ({\n req,\n}: {\n req: PayloadRequest\n}): Promise<MCPOAuthTokens> => {\n const collectionConfig = req.payload.collections[req.user!.collection]!.config\n\n if (collectionConfig.auth.disableLocalStrategy) {\n throw new APIError(\n `MCP clients can't get Payload login tokens for the \"${collectionConfig.slug}\" collection because its local strategy is disabled. Pass oauth.issueToken to the MCP plugin.`,\n )\n }\n\n // The full document, like the one a password login starts its session on\n const user = (await req.payload.db.findOne<NonNullable<PayloadRequest['user']>>({\n collection: collectionConfig.slug,\n req,\n where: { id: { equals: req.user!.id } },\n }))!\n\n const { refreshToken, token } = await createAuthToken({\n collectionConfig,\n email: user.email!,\n req,\n shouldCreateRefreshToken: true,\n user,\n })\n\n return {\n accessToken: sealLoginToken({ req, token }),\n expiresIn: collectionConfig.auth.tokenExpiration,\n refreshToken,\n }\n}\n\n/** The default `refreshToken`. Payload logs out the session when a refresh token is reused. */\nexport const refreshLoginToken = async ({\n refreshToken,\n req,\n}: {\n refreshToken: string\n req: PayloadRequest\n}): Promise<MCPOAuthTokens | null> => {\n const refreshed = await redeemRefreshToken({ refreshToken, req })\n\n return (\n refreshed && {\n accessToken: sealLoginToken({ req, token: refreshed.token }),\n expiresIn: refreshed.collectionConfig.auth.tokenExpiration,\n refreshToken: refreshed.refreshToken,\n }\n )\n}\n\n/** Finds the user of an MCP request's access token from `issueLoginToken`. */\nexport const authenticateLoginToken = async ({\n req,\n token,\n}: {\n req: PayloadRequest\n token: string\n}): Promise<PayloadRequest['user']> => {\n const loginToken = unseal<{ token: string }>({ type: 'access', req, value: token })\n\n if (!loginToken) {\n return null\n }\n\n const headers = new Headers({\n Authorization: `JWT ${loginToken.token}`,\n DisableAutologin: 'true',\n })\n\n return (await executeAuthStrategies({ headers, payload: req.payload, req })).user\n}\n\nconst sealLoginToken = ({ req, token }: { req: PayloadRequest; token: string }): string =>\n seal({ type: 'access', data: { token }, req })\n"],"names":["APIError","createAuthToken","executeAuthStrategies","redeemRefreshToken","seal","unseal","issueLoginToken","req","collectionConfig","payload","collections","user","collection","config","auth","disableLocalStrategy","slug","db","findOne","where","id","equals","refreshToken","token","email","shouldCreateRefreshToken","accessToken","sealLoginToken","expiresIn","tokenExpiration","refreshLoginToken","refreshed","authenticateLoginToken","loginToken","type","value","headers","Headers","Authorization","DisableAutologin","data"],"mappings":"AAEA,SAASA,QAAQ,EAAEC,eAAe,EAAEC,qBAAqB,EAAEC,kBAAkB,QAAQ,UAAS;AAI9F,SAASC,IAAI,EAAEC,MAAM,QAAQ,mBAAkB;AAE/C;;;;CAIC,GACD,OAAO,MAAMC,kBAAkB,OAAO,EACpCC,GAAG,EAGJ;IACC,MAAMC,mBAAmBD,IAAIE,OAAO,CAACC,WAAW,CAACH,IAAII,IAAI,CAAEC,UAAU,CAAC,CAAEC,MAAM;IAE9E,IAAIL,iBAAiBM,IAAI,CAACC,oBAAoB,EAAE;QAC9C,MAAM,IAAIf,SACR,CAAC,oDAAoD,EAAEQ,iBAAiBQ,IAAI,CAAC,6FAA6F,CAAC;IAE/K;IAEA,yEAAyE;IACzE,MAAML,OAAQ,MAAMJ,IAAIE,OAAO,CAACQ,EAAE,CAACC,OAAO,CAAsC;QAC9EN,YAAYJ,iBAAiBQ,IAAI;QACjCT;QACAY,OAAO;YAAEC,IAAI;gBAAEC,QAAQd,IAAII,IAAI,CAAES,EAAE;YAAC;QAAE;IACxC;IAEA,MAAM,EAAEE,YAAY,EAAEC,KAAK,EAAE,GAAG,MAAMtB,gBAAgB;QACpDO;QACAgB,OAAOb,KAAKa,KAAK;QACjBjB;QACAkB,0BAA0B;QAC1Bd;IACF;IAEA,OAAO;QACLe,aAAaC,eAAe;YAAEpB;YAAKgB;QAAM;QACzCK,WAAWpB,iBAAiBM,IAAI,CAACe,eAAe;QAChDP;IACF;AACF,EAAC;AAED,6FAA6F,GAC7F,OAAO,MAAMQ,oBAAoB,OAAO,EACtCR,YAAY,EACZf,GAAG,EAIJ;IACC,MAAMwB,YAAY,MAAM5B,mBAAmB;QAAEmB;QAAcf;IAAI;IAE/D,OACEwB,aAAa;QACXL,aAAaC,eAAe;YAAEpB;YAAKgB,OAAOQ,UAAUR,KAAK;QAAC;QAC1DK,WAAWG,UAAUvB,gBAAgB,CAACM,IAAI,CAACe,eAAe;QAC1DP,cAAcS,UAAUT,YAAY;IACtC;AAEJ,EAAC;AAED,4EAA4E,GAC5E,OAAO,MAAMU,yBAAyB,OAAO,EAC3CzB,GAAG,EACHgB,KAAK,EAIN;IACC,MAAMU,aAAa5B,OAA0B;QAAE6B,MAAM;QAAU3B;QAAK4B,OAAOZ;IAAM;IAEjF,IAAI,CAACU,YAAY;QACf,OAAO;IACT;IAEA,MAAMG,UAAU,IAAIC,QAAQ;QAC1BC,eAAe,CAAC,IAAI,EAAEL,WAAWV,KAAK,EAAE;QACxCgB,kBAAkB;IACpB;IAEA,OAAO,AAAC,CAAA,MAAMrC,sBAAsB;QAAEkC;QAAS3B,SAASF,IAAIE,OAAO;QAAEF;IAAI,EAAC,EAAGI,IAAI;AACnF,EAAC;AAED,MAAMgB,iBAAiB,CAAC,EAAEpB,GAAG,EAAEgB,KAAK,EAA0C,GAC5EnB,KAAK;QAAE8B,MAAM;QAAUM,MAAM;YAAEjB;QAAM;QAAGhB;IAAI"}
@@ -0,0 +1,12 @@
1
+ import type { Endpoint } from 'payload';
2
+ /**
3
+ * Turns Payload into the OAuth authorization server for MCP clients when `oauth.type` is
4
+ * `'builtin'`. Clients register, the user allows them after logging in to the admin panel, and
5
+ * the client receives the tokens `issueToken` creates.
6
+ *
7
+ * Clients, consent forms, authorization codes, and refresh tokens are encrypted and handed to the
8
+ * client instead of being stored. Only the default tokens write to the database, to the user's
9
+ * login sessions.
10
+ */
11
+ export declare const oauthServerEndpoints: Endpoint[];
12
+ //# sourceMappingURL=oauthServer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauthServer.d.ts","sourceRoot":"","sources":["../../src/endpoint/oauthServer.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAkC,MAAM,SAAS,CAAA;AAoCvE;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,EAAE,QAAQ,EAqC1C,CAAA"}
@@ -0,0 +1,362 @@
1
+ import { OAuthErrorCode } from '@modelcontextprotocol/server';
2
+ import { createHash } from 'crypto';
3
+ import { formatAdminURL } from 'payload/shared';
4
+ import { getPluginConfig } from '../utils/getPluginConfig.js';
5
+ import { seal, unseal } from '../utils/seal.js';
6
+ import { getMCPURL } from './oauth.js';
7
+ import { issueLoginToken, refreshLoginToken } from './oauthLoginToken.js';
8
+ /**
9
+ * Turns Payload into the OAuth authorization server for MCP clients when `oauth.type` is
10
+ * `'builtin'`. Clients register, the user allows them after logging in to the admin panel, and
11
+ * the client receives the tokens `issueToken` creates.
12
+ *
13
+ * Clients, consent forms, authorization codes, and refresh tokens are encrypted and handed to the
14
+ * client instead of being stored. Only the default tokens write to the database, to the user's
15
+ * login sessions.
16
+ */ export const oauthServerEndpoints = [
17
+ {
18
+ disableAuth: true,
19
+ handler: (req)=>withIssuer(req, (issuer)=>metadataResponse(issuer)),
20
+ method: 'get',
21
+ // OpenID discovery is the only metadata location MCP clients check below the issuer's path,
22
+ // which lets this work under /api without a route at the site root
23
+ path: '/mcp/oauth/.well-known/openid-configuration'
24
+ },
25
+ {
26
+ disableAuth: true,
27
+ handler: (req)=>withIssuer(req, ()=>Response.json({
28
+ keys: []
29
+ })),
30
+ method: 'get',
31
+ path: '/mcp/oauth/jwks'
32
+ },
33
+ {
34
+ disableAuth: true,
35
+ handler: (req)=>withIssuer(req, ()=>register(req)),
36
+ method: 'post',
37
+ path: '/mcp/oauth/register'
38
+ },
39
+ {
40
+ handler: (req)=>withIssuer(req, ()=>authorize(req)),
41
+ method: 'get',
42
+ path: '/mcp/oauth/authorize'
43
+ },
44
+ {
45
+ handler: (req)=>withIssuer(req, ()=>approve(req)),
46
+ method: 'post',
47
+ path: '/mcp/oauth/authorize'
48
+ },
49
+ {
50
+ disableAuth: true,
51
+ handler: (req)=>withIssuer(req, ()=>token(req)),
52
+ method: 'post',
53
+ path: '/mcp/oauth/token'
54
+ }
55
+ ];
56
+ /** The authorization server URL, or `undefined` when Payload isn't the authorization server. */ const getIssuer = (req)=>getPluginConfig({
57
+ config: req.payload.config
58
+ }).oauth?.type === 'builtin' ? `${getMCPURL({
59
+ config: req.payload.config,
60
+ url: req.url
61
+ })}/oauth` : undefined;
62
+ const withIssuer = (req, handler)=>{
63
+ const issuer = getIssuer(req);
64
+ return issuer ? handler(issuer) : new Response(null, {
65
+ status: 404
66
+ });
67
+ };
68
+ /**
69
+ * The user logged in to this browser, who can allow MCP clients. Tokens and API keys in the
70
+ * `Authorization` header can't, or an MCP client could use its access token to approve itself a
71
+ * new, separate login.
72
+ */ const getBrowserUser = (req)=>req.headers.has('Authorization') ? null : req.user;
73
+ const metadataResponse = (issuer)=>Response.json({
74
+ authorization_endpoint: `${issuer}/authorize`,
75
+ code_challenge_methods_supported: [
76
+ 'S256'
77
+ ],
78
+ grant_types_supported: [
79
+ 'authorization_code',
80
+ 'refresh_token'
81
+ ],
82
+ // Required by OpenID discovery. No ID tokens are issued.
83
+ id_token_signing_alg_values_supported: [
84
+ 'RS256'
85
+ ],
86
+ issuer,
87
+ jwks_uri: `${issuer}/jwks`,
88
+ registration_endpoint: `${issuer}/register`,
89
+ response_types_supported: [
90
+ 'code'
91
+ ],
92
+ subject_types_supported: [
93
+ 'public'
94
+ ],
95
+ token_endpoint: `${issuer}/token`,
96
+ token_endpoint_auth_methods_supported: [
97
+ 'none'
98
+ ]
99
+ }, // Not cached, since without `serverURL` its URLs come from the request
100
+ {
101
+ headers: {
102
+ 'Access-Control-Allow-Origin': '*',
103
+ 'Cache-Control': 'no-store'
104
+ }
105
+ });
106
+ const register = async (req)=>{
107
+ const { client_name, redirect_uris } = await req.json().catch(()=>({}));
108
+ if (!Array.isArray(redirect_uris) || !redirect_uris.length || !redirect_uris.every((uri)=>typeof uri === 'string' && URL.canParse(uri) && !uri.includes('#'))) {
109
+ return Response.json({
110
+ error: OAuthErrorCode.InvalidRedirectUri
111
+ }, {
112
+ status: 400
113
+ });
114
+ }
115
+ const client = {
116
+ name: typeof client_name === 'string' ? client_name : 'An MCP client',
117
+ redirectURIs: redirect_uris
118
+ };
119
+ return Response.json({
120
+ client_id: seal({
121
+ type: 'client',
122
+ data: client,
123
+ req
124
+ }),
125
+ client_name: client.name,
126
+ grant_types: [
127
+ 'authorization_code',
128
+ 'refresh_token'
129
+ ],
130
+ redirect_uris: client.redirectURIs,
131
+ response_types: [
132
+ 'code'
133
+ ],
134
+ token_endpoint_auth_method: 'none'
135
+ }, {
136
+ status: 201
137
+ });
138
+ };
139
+ const authorize = (req)=>{
140
+ const url = new URL(req.url);
141
+ const params = url.searchParams;
142
+ const clientID = params.get('client_id');
143
+ const client = unseal({
144
+ type: 'client',
145
+ req,
146
+ value: clientID
147
+ });
148
+ const redirectURI = params.get('redirect_uri');
149
+ // Never redirect to an address the client didn't register
150
+ if (!client || !redirectURI || !client.redirectURIs.includes(redirectURI)) {
151
+ return page({
152
+ content: '<p>Start the sign-in again from your MCP client.</p>',
153
+ status: 400,
154
+ title: 'This sign-in link is invalid'
155
+ });
156
+ }
157
+ const state = params.get('state');
158
+ const codeChallenge = params.get('code_challenge');
159
+ if (params.get('response_type') !== 'code' || params.get('code_challenge_method') !== 'S256' || !codeChallenge) {
160
+ return redirect({
161
+ params: {
162
+ error: OAuthErrorCode.InvalidRequest,
163
+ state
164
+ },
165
+ redirectURI
166
+ });
167
+ }
168
+ const user = getBrowserUser(req);
169
+ if (!user) {
170
+ const loginURL = formatAdminURL({
171
+ adminRoute: req.payload.config.routes.admin,
172
+ path: '/login'
173
+ });
174
+ return new Response(null, {
175
+ headers: {
176
+ Location: `${loginURL}?redirect=${encodeURIComponent(url.pathname + url.search)}`
177
+ },
178
+ status: 302
179
+ });
180
+ }
181
+ const consent = {
182
+ clientID: clientID,
183
+ codeChallenge,
184
+ collection: user.collection,
185
+ redirectURI,
186
+ state,
187
+ userID: user.id
188
+ };
189
+ return page({
190
+ content: `<p>It will be able to use this app's MCP tools as ${escapeHTML(String(user.email ?? user.id))}, and you will return to ${escapeHTML(new URL(redirectURI).host || redirectURI)}.</p>
191
+ <form method="post">
192
+ <input name="consent" type="hidden" value="${seal({
193
+ type: 'consent',
194
+ data: consent,
195
+ expiresIn: 20 * 60 * 60,
196
+ req
197
+ })}">
198
+ <button name="decision" type="submit" value="allow">Allow</button>
199
+ <button name="decision" type="submit" value="deny">Deny</button>
200
+ </form>`,
201
+ status: 200,
202
+ title: `Allow ${client.name} to use your account?`
203
+ });
204
+ };
205
+ const approve = async (req)=>{
206
+ const form = new URLSearchParams(await req.text());
207
+ const consent = unseal({
208
+ type: 'consent',
209
+ req,
210
+ value: form.get('consent')
211
+ });
212
+ const user = getBrowserUser(req);
213
+ // The form is only valid for the user it was shown to
214
+ if (!consent || user?.id !== consent.userID || user.collection !== consent.collection) {
215
+ return page({
216
+ content: '<p>Start the sign-in again from your MCP client.</p>',
217
+ status: 400,
218
+ title: 'This sign-in request expired'
219
+ });
220
+ }
221
+ const { clientID, codeChallenge, redirectURI, state } = consent;
222
+ if (form.get('decision') !== 'allow') {
223
+ return redirect({
224
+ params: {
225
+ error: OAuthErrorCode.AccessDenied,
226
+ state
227
+ },
228
+ redirectURI
229
+ });
230
+ }
231
+ const code = {
232
+ clientID,
233
+ codeChallenge,
234
+ redirectURI,
235
+ tokens: await getTokenHooks(req).issueToken({
236
+ req
237
+ })
238
+ };
239
+ return redirect({
240
+ params: {
241
+ code: seal({
242
+ type: 'code',
243
+ data: code,
244
+ expiresIn: 60,
245
+ req
246
+ }),
247
+ state
248
+ },
249
+ redirectURI
250
+ });
251
+ };
252
+ const token = async (req)=>{
253
+ const form = new URLSearchParams(await req.text());
254
+ const clientID = form.get('client_id');
255
+ const tokens = clientID && (form.get('grant_type') === 'refresh_token' ? await refresh({
256
+ clientID,
257
+ form,
258
+ req
259
+ }) : redeemCode({
260
+ clientID,
261
+ form,
262
+ req
263
+ }));
264
+ if (!clientID || !tokens) {
265
+ return Response.json({
266
+ error: OAuthErrorCode.InvalidGrant
267
+ }, {
268
+ status: 400
269
+ });
270
+ }
271
+ return Response.json({
272
+ access_token: tokens.accessToken,
273
+ expires_in: tokens.expiresIn,
274
+ refresh_token: tokens.refreshToken && seal({
275
+ type: 'refresh',
276
+ data: {
277
+ clientID,
278
+ refreshToken: tokens.refreshToken
279
+ },
280
+ req
281
+ }),
282
+ token_type: 'Bearer'
283
+ }, {
284
+ headers: {
285
+ 'Cache-Control': 'no-store'
286
+ }
287
+ });
288
+ };
289
+ const redeemCode = ({ clientID, form, req })=>{
290
+ const code = unseal({
291
+ type: 'code',
292
+ req,
293
+ value: form.get('code')
294
+ });
295
+ if (form.get('grant_type') === 'authorization_code' && code?.clientID === clientID && code.redirectURI === form.get('redirect_uri') && // PKCE: only the client that started the sign-in knows the verifier
296
+ createHash('sha256').update(form.get('code_verifier') ?? '').digest('base64url') === code.codeChallenge) {
297
+ return code.tokens;
298
+ }
299
+ };
300
+ const refresh = async ({ clientID, form, req })=>{
301
+ const refreshToken = unseal({
302
+ type: 'refresh',
303
+ req,
304
+ value: form.get('refresh_token')
305
+ });
306
+ // Only the client a refresh token was issued to can use it
307
+ if (refreshToken?.clientID === clientID) {
308
+ return await getTokenHooks(req).refreshToken?.({
309
+ refreshToken: refreshToken.refreshToken,
310
+ req
311
+ });
312
+ }
313
+ };
314
+ /** `issueToken` and `refreshToken`, or the defaults for Payload login tokens. */ const getTokenHooks = (req)=>{
315
+ const oauth = getPluginConfig({
316
+ config: req.payload.config
317
+ }).oauth;
318
+ return oauth.issueToken ? {
319
+ issueToken: oauth.issueToken,
320
+ refreshToken: oauth.refreshToken
321
+ } : {
322
+ issueToken: issueLoginToken,
323
+ refreshToken: refreshLoginToken
324
+ };
325
+ };
326
+ const redirect = ({ params, redirectURI })=>{
327
+ const location = new URL(redirectURI);
328
+ for (const [key, value] of Object.entries(params)){
329
+ if (value) {
330
+ location.searchParams.set(key, value);
331
+ }
332
+ }
333
+ return new Response(null, {
334
+ headers: {
335
+ Location: location.href
336
+ },
337
+ status: 302
338
+ });
339
+ };
340
+ const page = ({ content, status, title })=>new Response(`<!doctype html>
341
+ <html lang="en">
342
+ <head>
343
+ <meta charset="utf-8">
344
+ <meta name="viewport" content="width=device-width, initial-scale=1">
345
+ <title>${escapeHTML(title)}</title>
346
+ <style>
347
+ body { font-family: system-ui, sans-serif; margin: 4rem auto; max-width: 32rem; overflow-wrap: anywhere; padding: 0 1rem }
348
+ button { font: inherit; margin-inline-end: 0.5rem; padding: 0.5rem 1rem }
349
+ </style>
350
+ </head>
351
+ <body><h1>${escapeHTML(title)}</h1>${content}</body>
352
+ </html>`, {
353
+ headers: {
354
+ // Stop other sites from framing the page to trick users into clicking Allow
355
+ 'Content-Security-Policy': "frame-ancestors 'none'",
356
+ 'Content-Type': 'text/html; charset=utf-8'
357
+ },
358
+ status
359
+ });
360
+ const escapeHTML = (value)=>value.replace(/[&<>"']/g, (character)=>`&#${character.charCodeAt(0)};`);
361
+
362
+ //# sourceMappingURL=oauthServer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/endpoint/oauthServer.ts"],"sourcesContent":["import type {\n OAuthClientInformationFull,\n OAuthTokens,\n OpenIdProviderDiscoveryMetadata,\n} from '@modelcontextprotocol/server'\nimport type { Endpoint, PayloadHandler, PayloadRequest } from 'payload'\n\nimport { OAuthErrorCode } from '@modelcontextprotocol/server'\nimport { createHash } from 'crypto'\nimport { formatAdminURL } from 'payload/shared'\n\nimport type { MCPBuiltinOAuthConfig, MCPOAuthTokens } from '../types.js'\n\nimport { getPluginConfig } from '../utils/getPluginConfig.js'\nimport { seal, unseal } from '../utils/seal.js'\nimport { getMCPURL } from './oauth.js'\nimport { issueLoginToken, refreshLoginToken } from './oauthLoginToken.js'\n\ntype Client = { name: string; redirectURIs: string[] }\n\ntype Consent = {\n clientID: string\n codeChallenge: string\n collection: string\n redirectURI: string\n state: null | string\n userID: number | string\n}\n\ntype Code = {\n clientID: string\n codeChallenge: string\n redirectURI: string\n tokens: MCPOAuthTokens\n}\n\ntype RefreshToken = {\n clientID: string\n refreshToken: string\n}\n\n/**\n * Turns Payload into the OAuth authorization server for MCP clients when `oauth.type` is\n * `'builtin'`. Clients register, the user allows them after logging in to the admin panel, and\n * the client receives the tokens `issueToken` creates.\n *\n * Clients, consent forms, authorization codes, and refresh tokens are encrypted and handed to the\n * client instead of being stored. Only the default tokens write to the database, to the user's\n * login sessions.\n */\nexport const oauthServerEndpoints: Endpoint[] = [\n {\n disableAuth: true,\n handler: (req) => withIssuer(req, (issuer) => metadataResponse(issuer)),\n method: 'get',\n // OpenID discovery is the only metadata location MCP clients check below the issuer's path,\n // which lets this work under /api without a route at the site root\n path: '/mcp/oauth/.well-known/openid-configuration',\n },\n {\n disableAuth: true,\n handler: (req) => withIssuer(req, () => Response.json({ keys: [] })),\n method: 'get',\n path: '/mcp/oauth/jwks',\n },\n {\n disableAuth: true,\n handler: (req) => withIssuer(req, () => register(req)),\n method: 'post',\n path: '/mcp/oauth/register',\n },\n {\n handler: (req) => withIssuer(req, () => authorize(req)),\n method: 'get',\n path: '/mcp/oauth/authorize',\n },\n {\n handler: (req) => withIssuer(req, () => approve(req)),\n method: 'post',\n path: '/mcp/oauth/authorize',\n },\n {\n disableAuth: true,\n handler: (req) => withIssuer(req, () => token(req)),\n method: 'post',\n path: '/mcp/oauth/token',\n },\n]\n\n/** The authorization server URL, or `undefined` when Payload isn't the authorization server. */\nconst getIssuer = (req: PayloadRequest): string | undefined =>\n getPluginConfig({ config: req.payload.config }).oauth?.type === 'builtin'\n ? `${getMCPURL({ config: req.payload.config, url: req.url! })}/oauth`\n : undefined\n\nconst withIssuer = (\n req: PayloadRequest,\n handler: (issuer: string) => Promise<Response> | Response,\n): Promise<Response> | Response => {\n const issuer = getIssuer(req)\n return issuer ? handler(issuer) : new Response(null, { status: 404 })\n}\n\n/**\n * The user logged in to this browser, who can allow MCP clients. Tokens and API keys in the\n * `Authorization` header can't, or an MCP client could use its access token to approve itself a\n * new, separate login.\n */\nconst getBrowserUser = (req: PayloadRequest): PayloadRequest['user'] =>\n req.headers.has('Authorization') ? null : req.user\n\nconst metadataResponse = (issuer: string): Response =>\n Response.json(\n {\n authorization_endpoint: `${issuer}/authorize`,\n code_challenge_methods_supported: ['S256'],\n grant_types_supported: ['authorization_code', 'refresh_token'],\n // Required by OpenID discovery. No ID tokens are issued.\n id_token_signing_alg_values_supported: ['RS256'],\n issuer,\n jwks_uri: `${issuer}/jwks`,\n registration_endpoint: `${issuer}/register`,\n response_types_supported: ['code'],\n subject_types_supported: ['public'],\n token_endpoint: `${issuer}/token`,\n token_endpoint_auth_methods_supported: ['none'],\n } satisfies OpenIdProviderDiscoveryMetadata,\n // Not cached, since without `serverURL` its URLs come from the request\n { headers: { 'Access-Control-Allow-Origin': '*', 'Cache-Control': 'no-store' } },\n )\n\nconst register: PayloadHandler = async (req) => {\n const { client_name, redirect_uris } = await req.json!().catch(() => ({}))\n\n if (\n !Array.isArray(redirect_uris) ||\n !redirect_uris.length ||\n !redirect_uris.every(\n (uri) => typeof uri === 'string' && URL.canParse(uri) && !uri.includes('#'),\n )\n ) {\n return Response.json({ error: OAuthErrorCode.InvalidRedirectUri }, { status: 400 })\n }\n\n const client: Client = {\n name: typeof client_name === 'string' ? client_name : 'An MCP client',\n redirectURIs: redirect_uris,\n }\n\n return Response.json(\n {\n client_id: seal({ type: 'client', data: client, req }),\n client_name: client.name,\n grant_types: ['authorization_code', 'refresh_token'],\n redirect_uris: client.redirectURIs,\n response_types: ['code'],\n token_endpoint_auth_method: 'none',\n } satisfies OAuthClientInformationFull,\n { status: 201 },\n )\n}\n\nconst authorize: PayloadHandler = (req) => {\n const url = new URL(req.url!)\n const params = url.searchParams\n const clientID = params.get('client_id')\n const client = unseal<Client>({ type: 'client', req, value: clientID })\n const redirectURI = params.get('redirect_uri')\n\n // Never redirect to an address the client didn't register\n if (!client || !redirectURI || !client.redirectURIs.includes(redirectURI)) {\n return page({\n content: '<p>Start the sign-in again from your MCP client.</p>',\n status: 400,\n title: 'This sign-in link is invalid',\n })\n }\n\n const state = params.get('state')\n const codeChallenge = params.get('code_challenge')\n\n if (\n params.get('response_type') !== 'code' ||\n params.get('code_challenge_method') !== 'S256' ||\n !codeChallenge\n ) {\n return redirect({ params: { error: OAuthErrorCode.InvalidRequest, state }, redirectURI })\n }\n\n const user = getBrowserUser(req)\n\n if (!user) {\n const loginURL = formatAdminURL({ adminRoute: req.payload.config.routes.admin, path: '/login' })\n\n return new Response(null, {\n headers: {\n Location: `${loginURL}?redirect=${encodeURIComponent(url.pathname + url.search)}`,\n },\n status: 302,\n })\n }\n\n const consent: Consent = {\n clientID: clientID!,\n codeChallenge,\n collection: user.collection,\n redirectURI,\n state,\n userID: user.id,\n }\n\n return page({\n content: `<p>It will be able to use this app's MCP tools as ${escapeHTML(String(user.email ?? user.id))}, and you will return to ${escapeHTML(new URL(redirectURI).host || redirectURI)}.</p>\n<form method=\"post\">\n<input name=\"consent\" type=\"hidden\" value=\"${seal({ type: 'consent', data: consent, expiresIn: 20 * 60 * 60, req })}\">\n<button name=\"decision\" type=\"submit\" value=\"allow\">Allow</button>\n<button name=\"decision\" type=\"submit\" value=\"deny\">Deny</button>\n</form>`,\n status: 200,\n title: `Allow ${client.name} to use your account?`,\n })\n}\n\nconst approve: PayloadHandler = async (req) => {\n const form = new URLSearchParams(await req.text!())\n const consent = unseal<Consent>({ type: 'consent', req, value: form.get('consent') })\n const user = getBrowserUser(req)\n\n // The form is only valid for the user it was shown to\n if (!consent || user?.id !== consent.userID || user.collection !== consent.collection) {\n return page({\n content: '<p>Start the sign-in again from your MCP client.</p>',\n status: 400,\n title: 'This sign-in request expired',\n })\n }\n\n const { clientID, codeChallenge, redirectURI, state } = consent\n\n if (form.get('decision') !== 'allow') {\n return redirect({ params: { error: OAuthErrorCode.AccessDenied, state }, redirectURI })\n }\n\n const code: Code = {\n clientID,\n codeChallenge,\n redirectURI,\n tokens: await getTokenHooks(req).issueToken({ req }),\n }\n\n return redirect({\n params: { code: seal({ type: 'code', data: code, expiresIn: 60, req }), state },\n redirectURI,\n })\n}\n\nconst token: PayloadHandler = async (req) => {\n const form = new URLSearchParams(await req.text!())\n const clientID = form.get('client_id')\n const tokens =\n clientID &&\n (form.get('grant_type') === 'refresh_token'\n ? await refresh({ clientID, form, req })\n : redeemCode({ clientID, form, req }))\n\n if (!clientID || !tokens) {\n return Response.json({ error: OAuthErrorCode.InvalidGrant }, { status: 400 })\n }\n\n return Response.json(\n {\n access_token: tokens.accessToken,\n expires_in: tokens.expiresIn,\n refresh_token:\n tokens.refreshToken &&\n seal({\n type: 'refresh',\n data: { clientID, refreshToken: tokens.refreshToken } satisfies RefreshToken,\n req,\n }),\n token_type: 'Bearer',\n } satisfies OAuthTokens,\n { headers: { 'Cache-Control': 'no-store' } },\n )\n}\n\nconst redeemCode = ({\n clientID,\n form,\n req,\n}: {\n clientID: string\n form: URLSearchParams\n req: PayloadRequest\n}): MCPOAuthTokens | undefined => {\n const code = unseal<Code>({ type: 'code', req, value: form.get('code') })\n\n if (\n form.get('grant_type') === 'authorization_code' &&\n code?.clientID === clientID &&\n code.redirectURI === form.get('redirect_uri') &&\n // PKCE: only the client that started the sign-in knows the verifier\n createHash('sha256')\n .update(form.get('code_verifier') ?? '')\n .digest('base64url') === code.codeChallenge\n ) {\n return code.tokens\n }\n}\n\nconst refresh = async ({\n clientID,\n form,\n req,\n}: {\n clientID: string\n form: URLSearchParams\n req: PayloadRequest\n}): Promise<MCPOAuthTokens | null | undefined> => {\n const refreshToken = unseal<RefreshToken>({\n type: 'refresh',\n req,\n value: form.get('refresh_token'),\n })\n\n // Only the client a refresh token was issued to can use it\n if (refreshToken?.clientID === clientID) {\n return await getTokenHooks(req).refreshToken?.({\n refreshToken: refreshToken.refreshToken,\n req,\n })\n }\n}\n\n/** `issueToken` and `refreshToken`, or the defaults for Payload login tokens. */\nconst getTokenHooks = (req: PayloadRequest) => {\n const oauth = getPluginConfig({ config: req.payload.config }).oauth as MCPBuiltinOAuthConfig\n\n return oauth.issueToken\n ? { issueToken: oauth.issueToken, refreshToken: oauth.refreshToken }\n : { issueToken: issueLoginToken, refreshToken: refreshLoginToken }\n}\n\nconst redirect = ({\n params,\n redirectURI,\n}: {\n params: Record<string, null | string>\n redirectURI: string\n}): Response => {\n const location = new URL(redirectURI)\n\n for (const [key, value] of Object.entries(params)) {\n if (value) {\n location.searchParams.set(key, value)\n }\n }\n\n return new Response(null, { headers: { Location: location.href }, status: 302 })\n}\n\nconst page = ({\n content,\n status,\n title,\n}: {\n content: string\n status: number\n title: string\n}): Response =>\n new Response(\n `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>${escapeHTML(title)}</title>\n<style>\nbody { font-family: system-ui, sans-serif; margin: 4rem auto; max-width: 32rem; overflow-wrap: anywhere; padding: 0 1rem }\nbutton { font: inherit; margin-inline-end: 0.5rem; padding: 0.5rem 1rem }\n</style>\n</head>\n<body><h1>${escapeHTML(title)}</h1>${content}</body>\n</html>`,\n {\n headers: {\n // Stop other sites from framing the page to trick users into clicking Allow\n 'Content-Security-Policy': \"frame-ancestors 'none'\",\n 'Content-Type': 'text/html; charset=utf-8',\n },\n status,\n },\n )\n\nconst escapeHTML = (value: string): string =>\n value.replace(/[&<>\"']/g, (character) => `&#${character.charCodeAt(0)};`)\n"],"names":["OAuthErrorCode","createHash","formatAdminURL","getPluginConfig","seal","unseal","getMCPURL","issueLoginToken","refreshLoginToken","oauthServerEndpoints","disableAuth","handler","req","withIssuer","issuer","metadataResponse","method","path","Response","json","keys","register","authorize","approve","token","getIssuer","config","payload","oauth","type","url","undefined","status","getBrowserUser","headers","has","user","authorization_endpoint","code_challenge_methods_supported","grant_types_supported","id_token_signing_alg_values_supported","jwks_uri","registration_endpoint","response_types_supported","subject_types_supported","token_endpoint","token_endpoint_auth_methods_supported","client_name","redirect_uris","catch","Array","isArray","length","every","uri","URL","canParse","includes","error","InvalidRedirectUri","client","name","redirectURIs","client_id","data","grant_types","response_types","token_endpoint_auth_method","params","searchParams","clientID","get","value","redirectURI","page","content","title","state","codeChallenge","redirect","InvalidRequest","loginURL","adminRoute","routes","admin","Location","encodeURIComponent","pathname","search","consent","collection","userID","id","escapeHTML","String","email","host","expiresIn","form","URLSearchParams","text","AccessDenied","code","tokens","getTokenHooks","issueToken","refresh","redeemCode","InvalidGrant","access_token","accessToken","expires_in","refresh_token","refreshToken","token_type","update","digest","location","key","Object","entries","set","href","replace","character","charCodeAt"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- export { AccessField } from '../components/AccessField/index.client.js';
1
+ export {};
2
2
  //# sourceMappingURL=client.d.ts.map