@frogbotai/plugin-mcp 0.33.0 → 0.35.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 +3 -9
- package/dist/access.d.ts +8 -6
- package/dist/access.d.ts.map +1 -1
- package/dist/access.js +19 -81
- package/dist/access.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -14
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @frogbotai/plugin-mcp
|
|
2
2
|
|
|
3
|
-
Expose
|
|
3
|
+
Expose your app's collections to external MCP clients.
|
|
4
4
|
|
|
5
5
|
```ts
|
|
6
6
|
import { apiKeysPlugin } from '@frogbotai/plugin-api-keys';
|
|
@@ -13,16 +13,10 @@ export default buildConfig({
|
|
|
13
13
|
{ slug: 'users', auth: true, fields: [] },
|
|
14
14
|
{ slug: 'posts', fields: [{ name: 'title', type: 'text' }] },
|
|
15
15
|
],
|
|
16
|
-
plugins: [
|
|
17
|
-
rolesPlugin(),
|
|
18
|
-
apiKeysPlugin(),
|
|
19
|
-
mcpPlugin({
|
|
20
|
-
collections: { posts: { enabled: { find: true } } },
|
|
21
|
-
}),
|
|
22
|
-
],
|
|
16
|
+
plugins: [rolesPlugin(), apiKeysPlugin(), mcpPlugin()],
|
|
23
17
|
});
|
|
24
18
|
```
|
|
25
19
|
|
|
26
20
|
Connect MCP clients to `/api/mcp` with a Bearer token minted by `@frogbotai/plugin-api-keys`. Revoked, malformed, and unknown keys are rejected. The plugin requires `apiKeysPlugin()` earlier in the plugin array.
|
|
27
21
|
|
|
28
|
-
|
|
22
|
+
Every app collection is exposed, and each call runs as the key's owner under the collection's access control. FrogBot's own collections (chats, API keys, logs) are never exposed. SSE is not supported.
|
package/dist/access.d.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { MCPPluginConfig } from '@payloadcms/plugin-mcp';
|
|
1
|
+
import type { AuthorizedMCP, SanitizedMCPPluginConfig } from '@payloadcms/plugin-mcp';
|
|
3
2
|
import type { AuthStrategy } from 'frogbot';
|
|
4
3
|
import type { PayloadRequest } from 'payload';
|
|
5
|
-
type
|
|
4
|
+
type AuthorizeMcpOptions = {
|
|
6
5
|
collection: string;
|
|
7
6
|
strategy: AuthStrategy;
|
|
8
|
-
|
|
7
|
+
pluginConfig: SanitizedMCPPluginConfig;
|
|
9
8
|
req: PayloadRequest;
|
|
10
9
|
};
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Signs the request in with a FrogBot API key and returns the MCP items the key's user can reach,
|
|
12
|
+
* without FrogBot's own collections. Access is always enforced, whatever the request asks.
|
|
13
|
+
*/
|
|
14
|
+
export declare function authorizeMcp({ collection, strategy, pluginConfig, req, }: AuthorizeMcpOptions): Promise<AuthorizedMCP>;
|
|
13
15
|
export {};
|
|
14
16
|
//# sourceMappingURL=access.d.ts.map
|
package/dist/access.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAEtF,OAAO,KAAK,EAAE,YAAY,EAAsB,MAAM,SAAS,CAAC;AAMhE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C,KAAK,mBAAmB,GAAG;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,YAAY,CAAC;IACvB,YAAY,EAAE,wBAAwB,CAAC;IACvC,GAAG,EAAE,cAAc,CAAC;CACrB,CAAC;AAEF;;;GAGG;AACH,wBAAsB,YAAY,CAAC,EACjC,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,GAAG,GACJ,EAAE,mBAAmB,GAAG,OAAO,CAAC,aAAa,CAAC,CAkC9C"}
|
package/dist/access.js
CHANGED
|
@@ -1,65 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { filterMCPItems } from '@payloadcms/plugin-mcp/internal';
|
|
2
|
+
import { attachRegisteredFrogBot, executeAuthStrategy, isInternalCollection } from 'frogbot/internal';
|
|
2
3
|
import { UnauthorizedError } from 'payload';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
'config',
|
|
8
|
-
'jobs'
|
|
9
|
-
]);
|
|
10
|
-
for (const [type, entities] of [
|
|
11
|
-
[
|
|
12
|
-
'Collection',
|
|
13
|
-
pluginOptions.collections
|
|
14
|
-
],
|
|
15
|
-
[
|
|
16
|
-
'Global',
|
|
17
|
-
pluginOptions.globals
|
|
18
|
-
]
|
|
19
|
-
]){
|
|
20
|
-
for (const slug of Object.keys(entities ?? {})){
|
|
21
|
-
const capability = toCamelCase(slug);
|
|
22
|
-
if (reserved.has(capability)) {
|
|
23
|
-
throw new Error(`[plugin-mcp] ${type} slug '${slug}' maps to reserved MCP capability '${capability}'.`);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
function collectionGrants(options) {
|
|
29
|
-
return Object.fromEntries(Object.entries(options ?? {}).map(([slug, config])=>[
|
|
30
|
-
toCamelCase(slug),
|
|
31
|
-
config?.enabled === true ? {
|
|
32
|
-
create: true,
|
|
33
|
-
delete: true,
|
|
34
|
-
find: true,
|
|
35
|
-
update: true
|
|
36
|
-
} : config?.enabled
|
|
37
|
-
]));
|
|
38
|
-
}
|
|
39
|
-
function globalGrants(options) {
|
|
40
|
-
return Object.fromEntries(Object.entries(options ?? {}).map(([slug, config])=>[
|
|
41
|
-
toCamelCase(slug),
|
|
42
|
-
config?.enabled === true ? {
|
|
43
|
-
find: true,
|
|
44
|
-
update: true
|
|
45
|
-
} : config?.enabled
|
|
46
|
-
]));
|
|
47
|
-
}
|
|
48
|
-
function namedGrants(items) {
|
|
49
|
-
return Object.fromEntries((items ?? []).map(({ name })=>[
|
|
50
|
-
toCamelCase(name),
|
|
51
|
-
true
|
|
52
|
-
]));
|
|
53
|
-
}
|
|
54
|
-
export async function resolveMcpAccess({ collection, strategy, pluginOptions, req }) {
|
|
4
|
+
/**
|
|
5
|
+
* Signs the request in with a FrogBot API key and returns the MCP items the key's user can reach,
|
|
6
|
+
* without FrogBot's own collections. Access is always enforced, whatever the request asks.
|
|
7
|
+
*/ export async function authorizeMcp({ collection, strategy, pluginConfig, req }) {
|
|
55
8
|
const frogbotReq = attachRegisteredFrogBot(req);
|
|
56
|
-
const frogbot = frogbotReq.frogbot;
|
|
57
9
|
let result;
|
|
58
10
|
try {
|
|
59
11
|
result = await executeAuthStrategy({
|
|
60
12
|
collection,
|
|
61
13
|
strategy,
|
|
62
|
-
frogbot,
|
|
14
|
+
frogbot: frogbotReq.frogbot,
|
|
63
15
|
headers: req.headers,
|
|
64
16
|
req: frogbotReq,
|
|
65
17
|
strategyName: strategy.name
|
|
@@ -68,33 +20,19 @@ export async function resolveMcpAccess({ collection, strategy, pluginOptions, re
|
|
|
68
20
|
throw new UnauthorizedError();
|
|
69
21
|
}
|
|
70
22
|
if (!result.user) throw new UnauthorizedError();
|
|
23
|
+
req.user = {
|
|
24
|
+
...result.user,
|
|
25
|
+
collection: result.user.collection ?? collection
|
|
26
|
+
};
|
|
27
|
+
const internal = new Set(req.payload.config.collections.filter(isInternalCollection).map(({ slug })=>slug));
|
|
28
|
+
const items = await filterMCPItems({
|
|
29
|
+
items: pluginConfig.items.filter((item)=>item.type !== 'collectionTool' || !internal.has(item.collectionSlug)),
|
|
30
|
+
overrideAccess: false,
|
|
31
|
+
req
|
|
32
|
+
});
|
|
71
33
|
return {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
collection: result.user.collection ?? collection
|
|
75
|
-
},
|
|
76
|
-
...collectionGrants(pluginOptions.collections),
|
|
77
|
-
...globalGrants(pluginOptions.globals),
|
|
78
|
-
'payload-mcp-tool': namedGrants(pluginOptions.mcp?.tools),
|
|
79
|
-
'payload-mcp-prompt': namedGrants(pluginOptions.mcp?.prompts),
|
|
80
|
-
'payload-mcp-resource': namedGrants(pluginOptions.mcp?.resources),
|
|
81
|
-
auth: {
|
|
82
|
-
auth: false,
|
|
83
|
-
forgotPassword: false,
|
|
84
|
-
login: false,
|
|
85
|
-
resetPassword: false,
|
|
86
|
-
unlock: false,
|
|
87
|
-
verify: false
|
|
88
|
-
},
|
|
89
|
-
config: {
|
|
90
|
-
find: false,
|
|
91
|
-
update: false
|
|
92
|
-
},
|
|
93
|
-
jobs: {
|
|
94
|
-
create: false,
|
|
95
|
-
run: false,
|
|
96
|
-
update: false
|
|
97
|
-
}
|
|
34
|
+
items,
|
|
35
|
+
overrideAccess: false
|
|
98
36
|
};
|
|
99
37
|
}
|
|
100
38
|
|
package/dist/access.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/access.ts"],"names":["
|
|
1
|
+
{"version":3,"sources":["../src/access.ts"],"names":["filterMCPItems","attachRegisteredFrogBot","executeAuthStrategy","isInternalCollection","UnauthorizedError","authorizeMcp","collection","strategy","pluginConfig","req","frogbotReq","result","frogbot","headers","strategyName","name","user","internal","Set","payload","config","collections","filter","map","slug","items","item","type","has","collectionSlug","overrideAccess"],"mappings":"AACA,SAASA,cAAc,QAAQ,kCAAkC;AAEjE,SACEC,uBAAuB,EACvBC,mBAAmB,EACnBC,oBAAoB,QACf,mBAAmB;AAE1B,SAASC,iBAAiB,QAAQ,UAAU;AAS5C;;;CAGC,GACD,OAAO,eAAeC,aAAa,EACjCC,UAAU,EACVC,QAAQ,EACRC,YAAY,EACZC,GAAG,EACiB;IACpB,MAAMC,aAAaT,wBAAwBQ;IAC3C,IAAIE;IAEJ,IAAI;QACFA,SAAS,MAAMT,oBAAoB;YACjCI;YACAC;YACAK,SAASF,WAAWE,OAAO;YAC3BC,SAASJ,IAAII,OAAO;YACpBJ,KAAKC;YACLI,cAAcP,SAASQ,IAAI;QAC7B;IACF,EAAE,OAAM;QACN,MAAM,IAAIX;IACZ;IAEA,IAAI,CAACO,OAAOK,IAAI,EAAE,MAAM,IAAIZ;IAE5BK,IAAIO,IAAI,GAAG;QAAE,GAAGL,OAAOK,IAAI;QAAEV,YAAYK,OAAOK,IAAI,CAACV,UAAU,IAAIA;IAAW;IAE9E,MAAMW,WAAW,IAAIC,IACnBT,IAAIU,OAAO,CAACC,MAAM,CAACC,WAAW,CAACC,MAAM,CAACnB,sBAAsBoB,GAAG,CAAC,CAAC,EAAEC,IAAI,EAAE,GAAKA;IAGhF,MAAMC,QAAQ,MAAMzB,eAAe;QACjCyB,OAAOjB,aAAaiB,KAAK,CAACH,MAAM,CAC9B,CAACI,OAASA,KAAKC,IAAI,KAAK,oBAAoB,CAACV,SAASW,GAAG,CAACF,KAAKG,cAAc;QAE/EC,gBAAgB;QAChBrB;IACF;IAEA,OAAO;QAAEgB;QAAOK,gBAAgB;IAAM;AACxC","file":"access.js"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { MCPPluginConfig } from '@payloadcms/plugin-mcp';
|
|
2
2
|
import type { Plugin } from 'frogbot';
|
|
3
|
-
export
|
|
4
|
-
export
|
|
3
|
+
export { defineCollectionTool, defineGlobalTool, definePrompt, defineTool, } from '@payloadcms/plugin-mcp';
|
|
4
|
+
export type McpPluginOptions = Omit<MCPPluginConfig, 'overrideGetAuthorizedMCP'>;
|
|
5
|
+
export declare function mcpPlugin(options?: McpPluginOptions): Plugin;
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAItC,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,YAAY,EACZ,UAAU,GACX,MAAM,wBAAwB,CAAC;AAEhC,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,eAAe,EAAE,0BAA0B,CAAC,CAAC;AAEjF,wBAAgB,SAAS,CAAC,OAAO,GAAE,gBAAqB,GAAG,MAAM,CAkChE"}
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { isApiKeyStrategy } from '@frogbotai/plugin-api-keys';
|
|
2
2
|
import { mcpPlugin as payloadMcpPlugin } from '@payloadcms/plugin-mcp';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
export function mcpPlugin(
|
|
6
|
-
|
|
7
|
-
return async (config)=>{
|
|
3
|
+
import { authorizeMcp } from './access.js';
|
|
4
|
+
export { defineCollectionTool, defineGlobalTool, definePrompt, defineTool } from '@payloadcms/plugin-mcp';
|
|
5
|
+
export function mcpPlugin(options = {}) {
|
|
6
|
+
return (config)=>{
|
|
8
7
|
const apiKeyStrategy = config.collections.flatMap((collection)=>{
|
|
9
8
|
const auth = typeof collection.auth === 'object' ? collection.auth : undefined;
|
|
10
9
|
return (auth?.strategies ?? []).map((strategy)=>({
|
|
@@ -15,16 +14,21 @@ export function mcpPlugin(pluginOptions = {}) {
|
|
|
15
14
|
if (!apiKeyStrategy) {
|
|
16
15
|
throw new Error('[plugin-mcp] apiKeysPlugin must be configured before mcpPlugin.');
|
|
17
16
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
return {
|
|
18
|
+
...config,
|
|
19
|
+
_payloadPlugins: [
|
|
20
|
+
...config._payloadPlugins ?? [],
|
|
21
|
+
payloadMcpPlugin({
|
|
22
|
+
...options,
|
|
23
|
+
overrideGetAuthorizedMCP: ({ pluginConfig, req })=>authorizeMcp({
|
|
24
|
+
collection: apiKeyStrategy.collection,
|
|
25
|
+
strategy: apiKeyStrategy.strategy,
|
|
26
|
+
pluginConfig,
|
|
27
|
+
req
|
|
28
|
+
})
|
|
26
29
|
})
|
|
27
|
-
|
|
30
|
+
]
|
|
31
|
+
};
|
|
28
32
|
};
|
|
29
33
|
}
|
|
30
34
|
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["isApiKeyStrategy","mcpPlugin","payloadMcpPlugin","
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["isApiKeyStrategy","mcpPlugin","payloadMcpPlugin","authorizeMcp","defineCollectionTool","defineGlobalTool","definePrompt","defineTool","options","config","apiKeyStrategy","collections","flatMap","collection","auth","undefined","strategies","map","strategy","slug","find","Error","_payloadPlugins","overrideGetAuthorizedMCP","pluginConfig","req"],"mappings":"AAAA,SAASA,gBAAgB,QAAQ,6BAA6B;AAE9D,SAASC,aAAaC,gBAAgB,QAAQ,yBAAyB;AAGvE,SAASC,YAAY,QAAQ,cAAc;AAE3C,SACEC,oBAAoB,EACpBC,gBAAgB,EAChBC,YAAY,EACZC,UAAU,QACL,yBAAyB;AAIhC,OAAO,SAASN,UAAUO,UAA4B,CAAC,CAAC;IACtD,OAAO,CAACC;QACN,MAAMC,iBAAiBD,OAAOE,WAAW,CACtCC,OAAO,CAAC,CAACC;YACR,MAAMC,OAAO,OAAOD,WAAWC,IAAI,KAAK,WAAWD,WAAWC,IAAI,GAAGC;YAErE,OAAO,AAACD,CAAAA,MAAME,cAAc,EAAE,AAAD,EAAGC,GAAG,CAAC,CAACC,WAAc,CAAA;oBACjDL,YAAYA,WAAWM,IAAI;oBAC3BD;gBACF,CAAA;QACF,GACCE,IAAI,CAAC,CAAC,EAAEF,QAAQ,EAAE,GAAKlB,iBAAiBkB;QAE3C,IAAI,CAACR,gBAAgB;YACnB,MAAM,IAAIW,MAAM;QAClB;QAEA,OAAO;YACL,GAAGZ,MAAM;YACTa,iBAAiB;mBACXb,OAAOa,eAAe,IAAI,EAAE;gBAChCpB,iBAAiB;oBACf,GAAGM,OAAO;oBACVe,0BAA0B,CAAC,EAAEC,YAAY,EAAEC,GAAG,EAAE,GAC9CtB,aAAa;4BACXU,YAAYH,eAAeG,UAAU;4BACrCK,UAAUR,eAAeQ,QAAQ;4BACjCM;4BACAC;wBACF;gBACJ;aACD;QACH;IACF;AACF","file":"index.js"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frogbotai/plugin-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
4
4
|
"description": "Payload MCP integration for FrogBot.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Colby Gilbert",
|
|
@@ -24,22 +24,22 @@
|
|
|
24
24
|
"dist"
|
|
25
25
|
],
|
|
26
26
|
"engines": {
|
|
27
|
-
"node": ">=
|
|
27
|
+
"node": ">=24.21.0"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@payloadcms/plugin-mcp": "
|
|
30
|
+
"@payloadcms/plugin-mcp": "4.0.0-beta.2"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"payload": "
|
|
34
|
-
"
|
|
35
|
-
"
|
|
33
|
+
"payload": "4.0.0-beta.2",
|
|
34
|
+
"@frogbotai/plugin-api-keys": "0.35.0",
|
|
35
|
+
"frogbot": "0.35.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"payload": "
|
|
39
|
-
"typescript": "
|
|
40
|
-
"@frogbotai/plugin-
|
|
41
|
-
"@frogbotai/plugin-
|
|
42
|
-
"frogbot": "0.
|
|
38
|
+
"payload": "4.0.0-beta.2",
|
|
39
|
+
"typescript": "6.0.3",
|
|
40
|
+
"@frogbotai/plugin-api-keys": "0.35.0",
|
|
41
|
+
"@frogbotai/plugin-roles": "0.35.0",
|
|
42
|
+
"frogbot": "0.35.0"
|
|
43
43
|
},
|
|
44
44
|
"publishConfig": {
|
|
45
45
|
"access": "public"
|