payload-mcp-toolkit 0.7.0 → 0.7.4
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 +29 -8
- package/dist/api-keys.js +57 -21
- package/dist/api-keys.js.map +1 -1
- package/dist/auth-strategy.d.ts +18 -7
- package/dist/auth-strategy.js +54 -12
- package/dist/auth-strategy.js.map +1 -1
- package/dist/tools/_helpers.d.ts +34 -0
- package/dist/tools/_helpers.js +98 -0
- package/dist/tools/_helpers.js.map +1 -1
- package/dist/tools/publish-draft.js +33 -1
- package/dist/tools/publish-draft.js.map +1 -1
- package/dist/tools/publish-global-draft.js +30 -1
- package/dist/tools/publish-global-draft.js.map +1 -1
- package/package.json +29 -15
- package/dist/__tests__/api-keys.test.js +0 -292
- package/dist/__tests__/api-keys.test.js.map +0 -1
- package/dist/__tests__/auth-strategy.test.js +0 -681
- package/dist/__tests__/auth-strategy.test.js.map +0 -1
- package/dist/__tests__/conflict-detection.test.js +0 -69
- package/dist/__tests__/conflict-detection.test.js.map +0 -1
- package/dist/__tests__/delete-document.test.js +0 -70
- package/dist/__tests__/delete-document.test.js.map +0 -1
- package/dist/__tests__/endpoint.test.js +0 -143
- package/dist/__tests__/endpoint.test.js.map +0 -1
- package/dist/__tests__/find-document.test.js +0 -178
- package/dist/__tests__/find-document.test.js.map +0 -1
- package/dist/__tests__/find-global.test.js +0 -173
- package/dist/__tests__/find-global.test.js.map +0 -1
- package/dist/__tests__/global-versions.test.js +0 -183
- package/dist/__tests__/global-versions.test.js.map +0 -1
- package/dist/__tests__/hash.test.js +0 -58
- package/dist/__tests__/hash.test.js.map +0 -1
- package/dist/__tests__/index-integration.test.js +0 -191
- package/dist/__tests__/index-integration.test.js.map +0 -1
- package/dist/__tests__/introspection.test.js +0 -659
- package/dist/__tests__/introspection.test.js.map +0 -1
- package/dist/__tests__/patch-global-layout.test.js +0 -474
- package/dist/__tests__/patch-global-layout.test.js.map +0 -1
- package/dist/__tests__/patch-layout.test.js +0 -171
- package/dist/__tests__/patch-layout.test.js.map +0 -1
- package/dist/__tests__/registry.test.js +0 -795
- package/dist/__tests__/registry.test.js.map +0 -1
- package/dist/__tests__/resources.test.js +0 -139
- package/dist/__tests__/resources.test.js.map +0 -1
- package/dist/__tests__/update-global.test.js +0 -157
- package/dist/__tests__/update-global.test.js.map +0 -1
- package/dist/__tests__/url-validator.test.js +0 -326
- package/dist/__tests__/url-validator.test.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/__tests__/auth-strategy.test.ts"],"sourcesContent":["import { describe, it, expect, vi } from 'vitest'\r\nimport {\r\n createBearerStrategy,\r\n AUTH_STRATEGY_NAME,\r\n getApiKeyContext,\r\n composeScopes,\r\n} from '../auth-strategy'\r\nimport { hashKey } from '../hash'\r\n\r\nconst SECRET = 'test-payload-secret'\r\n\r\ninterface BuildPayloadOptions {\r\n rows: unknown[]\r\n findError?: Error\r\n updateError?: Error\r\n}\r\n\r\nfunction buildPayload(opts: BuildPayloadOptions) {\r\n const findMock = vi.fn(async () => {\r\n if (opts.findError) throw opts.findError\r\n return { docs: opts.rows, totalDocs: opts.rows.length }\r\n })\r\n const updateMock = vi.fn(async () => {\r\n if (opts.updateError) throw opts.updateError\r\n return {}\r\n })\r\n return {\r\n secret: SECRET,\r\n find: findMock,\r\n update: updateMock,\r\n logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },\r\n findMock,\r\n updateMock,\r\n }\r\n}\r\n\r\nfunction makeHeaders(token: string | null): { get: (name: string) => string | null } {\r\n return {\r\n get: (name: string) => {\r\n if (name.toLowerCase() === 'authorization') return token === null ? null : `Bearer ${token}`\r\n return null\r\n },\r\n }\r\n}\r\n\r\ndescribe('composeScopes', () => {\r\n const baseRow = { id: 'k', user: { id: 'u' } }\r\n\r\n it('returns null when no typed fields are populated (= full access)', () => {\r\n expect(composeScopes({ ...baseRow })).toBeNull()\r\n })\r\n\r\n it('builds KeyScopes from typed fields when populated', () => {\r\n const out = composeScopes({\r\n ...baseRow,\r\n preset: 'editor',\r\n toolDeny: ['safeDelete'],\r\n })\r\n expect(out).toEqual({\r\n preset: 'editor',\r\n tools: { deny: ['safeDelete'] },\r\n })\r\n })\r\n\r\n it('treats preset === \"custom\" as a UI sentinel and drops it from KeyScopes', () => {\r\n const out = composeScopes({\r\n ...baseRow,\r\n preset: 'custom',\r\n collectionScopes: [{ slug: 'posts', actions: ['read', 'create'] }],\r\n })\r\n expect(out).toEqual({\r\n collections: { posts: ['read', 'create'] },\r\n })\r\n })\r\n\r\n it('returns a deny-all sentinel when preset === \"custom\" with no overrides', () => {\r\n // Fail-closed contract: a freshly-created Custom key with empty\r\n // collectionScopes / toolAllow / toolDeny denies every dispatch instead\r\n // of falling through to the \"no scopes set = full access\" guard.\r\n expect(composeScopes({ ...baseRow, preset: 'custom' })).toEqual({\r\n collections: {},\r\n globals: {},\r\n tools: { allow: [] },\r\n })\r\n })\r\n\r\n it('honours partial custom overrides (only toolAllow) without injecting deny-all', () => {\r\n // Verifies the sentinel only fires when ALL override fields are empty.\r\n const out = composeScopes({\r\n ...baseRow,\r\n preset: 'custom',\r\n toolAllow: ['searchContent'],\r\n })\r\n expect(out).toEqual({ tools: { allow: ['searchContent'] } })\r\n })\r\n\r\n it('preserves an empty actions array as explicit-deny-all on a listed collection', () => {\r\n const out = composeScopes({\r\n ...baseRow,\r\n preset: 'custom',\r\n collectionScopes: [{ slug: 'posts', actions: [] }],\r\n })\r\n expect(out).toEqual({ collections: { posts: [] } })\r\n })\r\n\r\n it('filters invalid action values out of collectionScopes', () => {\r\n const out = composeScopes({\r\n ...baseRow,\r\n preset: 'custom',\r\n collectionScopes: [\r\n { slug: 'posts', actions: ['read', 'bogus', 1, 'update'] as unknown as string[] },\r\n ],\r\n })\r\n expect(out).toEqual({ collections: { posts: ['read', 'update'] } })\r\n })\r\n\r\n // ─── Globals (U9) ────────────────────────────────────────────────\r\n\r\n it('maps globalScopes to KeyScopes.globals', () => {\r\n const out = composeScopes({\r\n ...baseRow,\r\n preset: 'custom',\r\n globalScopes: [\r\n { slug: 'siteSettings', actions: ['read', 'update'] },\r\n { slug: 'footer', actions: ['read'] },\r\n ],\r\n })\r\n expect(out).toEqual({\r\n globals: { siteSettings: ['read', 'update'], footer: ['read'] },\r\n })\r\n })\r\n\r\n it('filters invalid global action values', () => {\r\n const out = composeScopes({\r\n ...baseRow,\r\n preset: 'custom',\r\n globalScopes: [\r\n { slug: 'siteSettings', actions: ['read', 'create', 'delete', 'update'] as unknown as string[] },\r\n ],\r\n })\r\n // Only read/update are valid on globals — create/delete dropped.\r\n expect(out).toEqual({ globals: { siteSettings: ['read', 'update'] } })\r\n })\r\n\r\n it('preset admin + globalScopes: null produces a preset-only KeyScopes (legacy v0.5 row)', () => {\r\n const out = composeScopes({ ...baseRow, preset: 'admin', globalScopes: null })\r\n expect(out).toEqual({ preset: 'admin' })\r\n })\r\n\r\n it('preset editor + globalScopes: [] (explicit empty) emits explicit deny-all on the globals axis', () => {\r\n // Axis-independent rule: an explicit empty array commits \"no globals\r\n // allowed\" even when a preset is set. Use globalScopes: null (or omit\r\n // the field) to fall through to the preset default.\r\n const out = composeScopes({ ...baseRow, preset: 'editor', globalScopes: [] })\r\n expect(out).toEqual({ preset: 'editor', globals: {} })\r\n })\r\n\r\n it('preset editor + collectionScopes: [] (explicit empty) emits explicit deny-all on the collections axis', () => {\r\n const out = composeScopes({ ...baseRow, preset: 'editor', collectionScopes: [] })\r\n expect(out).toEqual({ preset: 'editor', collections: {} })\r\n })\r\n\r\n it('preset custom + populated collectionScopes + explicit toolAllow: [] honours both axes', () => {\r\n // Closes the prior gap where toolAllow: [] (operator intent: \"no tools\r\n // allowed\") was treated identically to absent and silently dropped, so\r\n // the row authenticated with a collection scope but no tool gate.\r\n const out = composeScopes({\r\n ...baseRow,\r\n preset: 'custom',\r\n collectionScopes: [{ slug: 'posts', actions: ['read'] }],\r\n toolAllow: [],\r\n })\r\n expect(out).toEqual({\r\n collections: { posts: ['read'] },\r\n tools: { allow: [] },\r\n })\r\n })\r\n\r\n it('explicit toolDeny: [] carries no entries and emits nothing', () => {\r\n // toolDeny is a deny-list — an empty array has nothing to deny, so the\r\n // axis is dropped rather than emitting `tools.deny: []`.\r\n const out = composeScopes({ ...baseRow, preset: 'editor', toolDeny: [] })\r\n expect(out).toEqual({ preset: 'editor' })\r\n })\r\n\r\n it('widened deny-all sentinel: empty everywhere produces both maps and tools.allow=[]', () => {\r\n expect(\r\n composeScopes({\r\n ...baseRow,\r\n preset: 'custom',\r\n collectionScopes: [],\r\n globalScopes: [],\r\n toolAllow: [],\r\n toolDeny: [],\r\n }),\r\n ).toEqual({ collections: {}, globals: {}, tools: { allow: [] } })\r\n })\r\n\r\n it('partial custom override on the globals axis does NOT fire the sentinel', () => {\r\n const out = composeScopes({\r\n ...baseRow,\r\n preset: 'custom',\r\n globalScopes: [{ slug: 'siteSettings', actions: ['read'] }],\r\n })\r\n expect(out).toEqual({ globals: { siteSettings: ['read'] } })\r\n })\r\n\r\n it('combines toolAllow and toolDeny into tools.allow / tools.deny', () => {\r\n const out = composeScopes({\r\n ...baseRow,\r\n toolAllow: ['findDocument', 'searchContent'],\r\n toolDeny: ['deleteDocument'],\r\n })\r\n expect(out).toEqual({\r\n tools: { allow: ['findDocument', 'searchContent'], deny: ['deleteDocument'] },\r\n })\r\n })\r\n\r\n // ─── Legacy row-shape fallback (pre-0.6 → 0.6 transition) ─────────\r\n //\r\n // The {slug, actions} normalization landed mid-0.6 — pre-existing\r\n // {collection, actions} / {global, actions} rows are tolerated for one\r\n // release so locally-tested 0.6 fixtures keep authenticating. The\r\n // fallback is removed in v0.7.\r\n\r\n it('falls back to row.collection when row.slug is missing (legacy collectionScopes)', () => {\r\n const warn = vi.fn()\r\n const out = composeScopes(\r\n {\r\n ...baseRow,\r\n preset: 'custom',\r\n collectionScopes: [{ collection: 'posts', actions: ['read'] } as never],\r\n },\r\n { warn },\r\n )\r\n expect(out).toEqual({ collections: { posts: ['read'] } })\r\n })\r\n\r\n it('falls back to row.global when row.slug is missing (legacy globalScopes)', () => {\r\n const warn = vi.fn()\r\n const out = composeScopes(\r\n {\r\n ...baseRow,\r\n preset: 'custom',\r\n globalScopes: [{ global: 'siteSettings', actions: ['read'] } as never],\r\n },\r\n { warn },\r\n )\r\n expect(out).toEqual({ globals: { siteSettings: ['read'] } })\r\n })\r\n\r\n it('prefers row.slug over the legacy row.collection key when both are set', () => {\r\n const out = composeScopes({\r\n ...baseRow,\r\n preset: 'custom',\r\n collectionScopes: [\r\n { slug: 'posts', collection: 'stale-slug', actions: ['read'] } as never,\r\n ],\r\n })\r\n expect(out).toEqual({ collections: { posts: ['read'] } })\r\n })\r\n})\r\n\r\ndescribe('createBearerStrategy.authenticate', () => {\r\n const strategy = createBearerStrategy({\r\n collectionSlug: 'payload-mcp-api-keys',\r\n userCollection: 'users',\r\n })\r\n\r\n it('exports the documented strategy name', () => {\r\n expect(strategy.name).toBe(AUTH_STRATEGY_NAME)\r\n })\r\n\r\n it('returns null user when Authorization header is missing', async () => {\r\n const payload = buildPayload({ rows: [] })\r\n const result = await strategy.authenticate!({\r\n headers: makeHeaders(null) as unknown as Headers,\r\n payload: payload as never,\r\n } as never)\r\n expect(result).toEqual({ user: null })\r\n expect(payload.findMock).not.toHaveBeenCalled()\r\n })\r\n\r\n it('returns null user when scheme is not Bearer', async () => {\r\n const payload = buildPayload({ rows: [] })\r\n const result = await strategy.authenticate!({\r\n headers: { get: () => 'Basic abc' } as unknown as Headers,\r\n payload: payload as never,\r\n } as never)\r\n expect(result).toEqual({ user: null })\r\n })\r\n\r\n it('returns null user when no row matches the hashed token', async () => {\r\n const payload = buildPayload({ rows: [] })\r\n const result = await strategy.authenticate!({\r\n headers: makeHeaders('plaintext-key') as unknown as Headers,\r\n payload: payload as never,\r\n } as never)\r\n expect(result).toEqual({ user: null })\r\n expect(payload.findMock).toHaveBeenCalledWith(\r\n expect.objectContaining({\r\n collection: 'payload-mcp-api-keys',\r\n where: { apiKeyIndex: { equals: hashKey('plaintext-key', SECRET) } },\r\n }),\r\n )\r\n })\r\n\r\n it('returns null user when the matched row is revoked', async () => {\r\n const payload = buildPayload({\r\n rows: [\r\n {\r\n id: 'k1',\r\n user: { id: 'u1', email: 'a@b.com' },\r\n revokedAt: '2025-01-01T00:00:00Z',\r\n },\r\n ],\r\n })\r\n const result = await strategy.authenticate!({\r\n headers: makeHeaders('plaintext-key') as unknown as Headers,\r\n payload: payload as never,\r\n } as never)\r\n expect(result).toEqual({ user: null })\r\n })\r\n\r\n it('returns null user when the matched row has expired', async () => {\r\n const payload = buildPayload({\r\n rows: [\r\n {\r\n id: 'k1',\r\n user: { id: 'u1' },\r\n expiresAt: new Date(Date.now() - 60_000).toISOString(),\r\n },\r\n ],\r\n })\r\n const result = await strategy.authenticate!({\r\n headers: makeHeaders('plaintext-key') as unknown as Headers,\r\n payload: payload as never,\r\n } as never)\r\n expect(result).toEqual({ user: null })\r\n })\r\n\r\n it('returns null user when the linked user is missing', async () => {\r\n const payload = buildPayload({ rows: [{ id: 'k1', user: null }] })\r\n const result = await strategy.authenticate!({\r\n headers: makeHeaders('plaintext-key') as unknown as Headers,\r\n payload: payload as never,\r\n } as never)\r\n expect(result).toEqual({ user: null })\r\n })\r\n\r\n it('hydrates the user, key context, and fires lastUsedAt write on a happy match', async () => {\r\n const payload = buildPayload({\r\n rows: [\r\n {\r\n id: 'k1',\r\n user: { id: 'u1', email: 'a@b.com' },\r\n preset: 'admin',\r\n keyPrefix: 'abc12345',\r\n },\r\n ],\r\n })\r\n const result = await strategy.authenticate!({\r\n headers: makeHeaders('plaintext-key') as unknown as Headers,\r\n payload: payload as never,\r\n } as never)\r\n expect(result.user).toMatchObject({\r\n id: 'u1',\r\n email: 'a@b.com',\r\n collection: 'users',\r\n _strategy: AUTH_STRATEGY_NAME,\r\n _mcpKey: { keyId: 'k1', keyPrefix: 'abc12345', scopes: { preset: 'admin' } },\r\n })\r\n // lastUsedAt update is fire-and-forget; allow microtask to schedule.\r\n await new Promise((r) => setImmediate(r))\r\n expect(payload.updateMock).toHaveBeenCalledWith(\r\n expect.objectContaining({\r\n collection: 'payload-mcp-api-keys',\r\n id: 'k1',\r\n data: expect.objectContaining({ lastUsedAt: expect.any(String) }),\r\n }),\r\n )\r\n })\r\n\r\n it('hydrates _mcpKey.scopes from typed fields with collectionScopes + toolDeny', async () => {\r\n const payload = buildPayload({\r\n rows: [\r\n {\r\n id: 'k3',\r\n user: { id: 'u3' },\r\n keyPrefix: 'deadbeef',\r\n preset: 'custom',\r\n collectionScopes: [{ slug: 'posts', actions: ['read', 'update'] }],\r\n toolDeny: ['safeDelete'],\r\n },\r\n ],\r\n })\r\n const result = await strategy.authenticate!({\r\n headers: makeHeaders('plaintext-key') as unknown as Headers,\r\n payload: payload as never,\r\n } as never)\r\n expect((result.user as { _mcpKey: { scopes: unknown } })._mcpKey.scopes).toEqual({\r\n collections: { posts: ['read', 'update'] },\r\n tools: { deny: ['safeDelete'] },\r\n })\r\n })\r\n\r\n it('does not block auth if find() throws', async () => {\r\n const payload = buildPayload({ rows: [], findError: new Error('db down') })\r\n const result = await strategy.authenticate!({\r\n headers: makeHeaders('plaintext-key') as unknown as Headers,\r\n payload: payload as never,\r\n } as never)\r\n expect(result).toEqual({ user: null })\r\n expect(payload.logger.error).toHaveBeenCalled()\r\n })\r\n})\r\n\r\ndescribe('getApiKeyContext', () => {\r\n it('returns null for non-MCP requests', () => {\r\n expect(getApiKeyContext({ user: null } as never)).toBeNull()\r\n expect(getApiKeyContext({ user: { id: 'cookie-user' } } as never)).toBeNull()\r\n })\r\n\r\n it('returns the embedded key context when present', () => {\r\n const ctx = getApiKeyContext({\r\n user: {\r\n id: 'u1',\r\n _mcpKey: { keyId: 'k1', keyPrefix: 'abcd', scopes: { preset: 'editor' } },\r\n },\r\n } as never)\r\n expect(ctx).toEqual({ keyId: 'k1', keyPrefix: 'abcd', scopes: { preset: 'editor' } })\r\n })\r\n})\r\n"],"names":["describe","it","expect","vi","createBearerStrategy","AUTH_STRATEGY_NAME","getApiKeyContext","composeScopes","hashKey","SECRET","buildPayload","opts","findMock","fn","findError","docs","rows","totalDocs","length","updateMock","updateError","secret","find","update","logger","info","warn","error","makeHeaders","token","get","name","toLowerCase","baseRow","id","user","toBeNull","out","preset","toolDeny","toEqual","tools","deny","collectionScopes","slug","actions","collections","posts","globals","allow","toolAllow","globalScopes","siteSettings","footer","collection","global","strategy","collectionSlug","userCollection","toBe","payload","result","authenticate","headers","not","toHaveBeenCalled","toHaveBeenCalledWith","objectContaining","where","apiKeyIndex","equals","email","revokedAt","expiresAt","Date","now","toISOString","keyPrefix","toMatchObject","_strategy","_mcpKey","keyId","scopes","Promise","r","setImmediate","data","lastUsedAt","any","String","Error","ctx"],"mappings":"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"}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { assertNoUpstreamPlugin, assertNoSlugConflict } from '../conflict-detection';
|
|
3
|
-
describe('assertNoUpstreamPlugin', ()=>{
|
|
4
|
-
it('does nothing when plugins list is empty / undefined', ()=>{
|
|
5
|
-
expect(()=>assertNoUpstreamPlugin(undefined)).not.toThrow();
|
|
6
|
-
expect(()=>assertNoUpstreamPlugin([])).not.toThrow();
|
|
7
|
-
});
|
|
8
|
-
it('throws when a plugin function is named mcpPlugin', ()=>{
|
|
9
|
-
function mcpPlugin() {}
|
|
10
|
-
expect(()=>assertNoUpstreamPlugin([
|
|
11
|
-
mcpPlugin
|
|
12
|
-
])).toThrow(/standalone successor/);
|
|
13
|
-
});
|
|
14
|
-
it('throws when a plugin closure references @payloadcms/plugin-mcp', ()=>{
|
|
15
|
-
const wrapper = function() {
|
|
16
|
-
// simulate the upstream plugin reference inside the closure
|
|
17
|
-
const _u = '@payloadcms/plugin-mcp';
|
|
18
|
-
return _u;
|
|
19
|
-
};
|
|
20
|
-
expect(()=>assertNoUpstreamPlugin([
|
|
21
|
-
wrapper
|
|
22
|
-
])).toThrow();
|
|
23
|
-
});
|
|
24
|
-
it('does not throw on unrelated plugin functions', ()=>{
|
|
25
|
-
function someOtherPlugin() {
|
|
26
|
-
const _x = 'totally unrelated';
|
|
27
|
-
return _x;
|
|
28
|
-
}
|
|
29
|
-
expect(()=>assertNoUpstreamPlugin([
|
|
30
|
-
someOtherPlugin
|
|
31
|
-
])).not.toThrow();
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
describe('assertNoSlugConflict', ()=>{
|
|
35
|
-
it('does nothing when no collections are present', ()=>{
|
|
36
|
-
expect(()=>assertNoSlugConflict(undefined)).not.toThrow();
|
|
37
|
-
expect(()=>assertNoSlugConflict([])).not.toThrow();
|
|
38
|
-
});
|
|
39
|
-
it('throws when the api-keys slug is already taken by another collection', ()=>{
|
|
40
|
-
expect(()=>assertNoSlugConflict([
|
|
41
|
-
{
|
|
42
|
-
slug: 'payload-mcp-api-keys',
|
|
43
|
-
fields: []
|
|
44
|
-
}
|
|
45
|
-
])).toThrow(/payload-mcp-api-keys/);
|
|
46
|
-
});
|
|
47
|
-
it('respects a custom slug override', ()=>{
|
|
48
|
-
expect(()=>assertNoSlugConflict([
|
|
49
|
-
{
|
|
50
|
-
slug: 'custom-keys',
|
|
51
|
-
fields: []
|
|
52
|
-
}
|
|
53
|
-
], 'custom-keys')).toThrow(/custom-keys/);
|
|
54
|
-
});
|
|
55
|
-
it('does not throw on unrelated collections', ()=>{
|
|
56
|
-
expect(()=>assertNoSlugConflict([
|
|
57
|
-
{
|
|
58
|
-
slug: 'posts',
|
|
59
|
-
fields: []
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
slug: 'media',
|
|
63
|
-
fields: []
|
|
64
|
-
}
|
|
65
|
-
])).not.toThrow();
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
//# sourceMappingURL=conflict-detection.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/__tests__/conflict-detection.test.ts"],"sourcesContent":["import { describe, it, expect } from 'vitest'\r\nimport { assertNoUpstreamPlugin, assertNoSlugConflict } from '../conflict-detection'\r\n\r\ndescribe('assertNoUpstreamPlugin', () => {\r\n it('does nothing when plugins list is empty / undefined', () => {\r\n expect(() => assertNoUpstreamPlugin(undefined)).not.toThrow()\r\n expect(() => assertNoUpstreamPlugin([])).not.toThrow()\r\n })\r\n\r\n it('throws when a plugin function is named mcpPlugin', () => {\r\n function mcpPlugin() {}\r\n expect(() => assertNoUpstreamPlugin([mcpPlugin as never])).toThrow(/standalone successor/)\r\n })\r\n\r\n it('throws when a plugin closure references @payloadcms/plugin-mcp', () => {\r\n const wrapper = function () {\r\n // simulate the upstream plugin reference inside the closure\r\n const _u = '@payloadcms/plugin-mcp'\r\n return _u\r\n }\r\n expect(() => assertNoUpstreamPlugin([wrapper as never])).toThrow()\r\n })\r\n\r\n it('does not throw on unrelated plugin functions', () => {\r\n function someOtherPlugin() {\r\n const _x = 'totally unrelated'\r\n return _x\r\n }\r\n expect(() => assertNoUpstreamPlugin([someOtherPlugin as never])).not.toThrow()\r\n })\r\n})\r\n\r\ndescribe('assertNoSlugConflict', () => {\r\n it('does nothing when no collections are present', () => {\r\n expect(() => assertNoSlugConflict(undefined)).not.toThrow()\r\n expect(() => assertNoSlugConflict([])).not.toThrow()\r\n })\r\n\r\n it('throws when the api-keys slug is already taken by another collection', () => {\r\n expect(() =>\r\n assertNoSlugConflict([{ slug: 'payload-mcp-api-keys', fields: [] } as never]),\r\n ).toThrow(/payload-mcp-api-keys/)\r\n })\r\n\r\n it('respects a custom slug override', () => {\r\n expect(() =>\r\n assertNoSlugConflict([{ slug: 'custom-keys', fields: [] } as never], 'custom-keys'),\r\n ).toThrow(/custom-keys/)\r\n })\r\n\r\n it('does not throw on unrelated collections', () => {\r\n expect(() =>\r\n assertNoSlugConflict([\r\n { slug: 'posts', fields: [] } as never,\r\n { slug: 'media', fields: [] } as never,\r\n ]),\r\n ).not.toThrow()\r\n })\r\n})\r\n"],"names":["describe","it","expect","assertNoUpstreamPlugin","assertNoSlugConflict","undefined","not","toThrow","mcpPlugin","wrapper","_u","someOtherPlugin","_x","slug","fields"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,EAAE,EAAEC,MAAM,QAAQ,SAAQ;AAC7C,SAASC,sBAAsB,EAAEC,oBAAoB,QAAQ,wBAAuB;AAEpFJ,SAAS,0BAA0B;IACjCC,GAAG,uDAAuD;QACxDC,OAAO,IAAMC,uBAAuBE,YAAYC,GAAG,CAACC,OAAO;QAC3DL,OAAO,IAAMC,uBAAuB,EAAE,GAAGG,GAAG,CAACC,OAAO;IACtD;IAEAN,GAAG,oDAAoD;QACrD,SAASO,aAAa;QACtBN,OAAO,IAAMC,uBAAuB;gBAACK;aAAmB,GAAGD,OAAO,CAAC;IACrE;IAEAN,GAAG,kEAAkE;QACnE,MAAMQ,UAAU;YACd,4DAA4D;YAC5D,MAAMC,KAAK;YACX,OAAOA;QACT;QACAR,OAAO,IAAMC,uBAAuB;gBAACM;aAAiB,GAAGF,OAAO;IAClE;IAEAN,GAAG,gDAAgD;QACjD,SAASU;YACP,MAAMC,KAAK;YACX,OAAOA;QACT;QACAV,OAAO,IAAMC,uBAAuB;gBAACQ;aAAyB,GAAGL,GAAG,CAACC,OAAO;IAC9E;AACF;AAEAP,SAAS,wBAAwB;IAC/BC,GAAG,gDAAgD;QACjDC,OAAO,IAAME,qBAAqBC,YAAYC,GAAG,CAACC,OAAO;QACzDL,OAAO,IAAME,qBAAqB,EAAE,GAAGE,GAAG,CAACC,OAAO;IACpD;IAEAN,GAAG,wEAAwE;QACzEC,OAAO,IACLE,qBAAqB;gBAAC;oBAAES,MAAM;oBAAwBC,QAAQ,EAAE;gBAAC;aAAW,GAC5EP,OAAO,CAAC;IACZ;IAEAN,GAAG,mCAAmC;QACpCC,OAAO,IACLE,qBAAqB;gBAAC;oBAAES,MAAM;oBAAeC,QAAQ,EAAE;gBAAC;aAAW,EAAE,gBACrEP,OAAO,CAAC;IACZ;IAEAN,GAAG,2CAA2C;QAC5CC,OAAO,IACLE,qBAAqB;gBACnB;oBAAES,MAAM;oBAASC,QAAQ,EAAE;gBAAC;gBAC5B;oBAAED,MAAM;oBAASC,QAAQ,EAAE;gBAAC;aAC7B,GACDR,GAAG,CAACC,OAAO;IACf;AACF"}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
-
import { createDeleteDocumentTool } from '../tools/delete-document';
|
|
3
|
-
const fakeSchema = {
|
|
4
|
-
slug: 'posts',
|
|
5
|
-
fields: [],
|
|
6
|
-
searchableFields: [],
|
|
7
|
-
hasDrafts: false
|
|
8
|
-
};
|
|
9
|
-
function buildReq() {
|
|
10
|
-
return {
|
|
11
|
-
payload: {
|
|
12
|
-
delete: vi.fn(),
|
|
13
|
-
logger: {
|
|
14
|
-
info: vi.fn(),
|
|
15
|
-
warn: vi.fn(),
|
|
16
|
-
error: vi.fn()
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
context: {},
|
|
20
|
-
user: null
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
describe('deleteDocument', ()=>{
|
|
24
|
-
const schemas = new Map([
|
|
25
|
-
[
|
|
26
|
-
'posts',
|
|
27
|
-
fakeSchema
|
|
28
|
-
]
|
|
29
|
-
]);
|
|
30
|
-
it('deletes via payload.delete with overrideAccess:false and returns success text', async ()=>{
|
|
31
|
-
const tool = createDeleteDocumentTool(schemas);
|
|
32
|
-
const req = buildReq();
|
|
33
|
-
req.payload.delete.mockResolvedValue({
|
|
34
|
-
id: 'a',
|
|
35
|
-
name: 'Hello'
|
|
36
|
-
});
|
|
37
|
-
const result = await tool.handler({
|
|
38
|
-
collection: 'posts',
|
|
39
|
-
id: 'a'
|
|
40
|
-
}, req, {});
|
|
41
|
-
expect(req.payload.delete).toHaveBeenCalledWith(expect.objectContaining({
|
|
42
|
-
collection: 'posts',
|
|
43
|
-
id: 'a',
|
|
44
|
-
overrideAccess: false
|
|
45
|
-
}));
|
|
46
|
-
expect(result.content[0].text).toMatch(/Deleted "Hello"/);
|
|
47
|
-
});
|
|
48
|
-
it('returns text error response when the doc does not exist (no throw)', async ()=>{
|
|
49
|
-
const tool = createDeleteDocumentTool(schemas);
|
|
50
|
-
const req = buildReq();
|
|
51
|
-
req.payload.delete.mockRejectedValue(new Error('not found'));
|
|
52
|
-
const result = await tool.handler({
|
|
53
|
-
collection: 'posts',
|
|
54
|
-
id: 'missing'
|
|
55
|
-
}, req, {});
|
|
56
|
-
expect(result.content[0].text).toMatch(/Error deleting missing from posts/);
|
|
57
|
-
});
|
|
58
|
-
it('rejects unknown collections at the boundary', async ()=>{
|
|
59
|
-
const tool = createDeleteDocumentTool(schemas);
|
|
60
|
-
const req = buildReq();
|
|
61
|
-
const result = await tool.handler({
|
|
62
|
-
collection: 'unknown',
|
|
63
|
-
id: 'x'
|
|
64
|
-
}, req, {});
|
|
65
|
-
expect(result.content[0].text).toMatch(/Unknown collection/);
|
|
66
|
-
expect(req.payload.delete).not.toHaveBeenCalled();
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
//# sourceMappingURL=delete-document.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/__tests__/delete-document.test.ts"],"sourcesContent":["import { describe, it, expect, vi } from 'vitest'\r\nimport { createDeleteDocumentTool } from '../tools/delete-document'\r\nimport type { CollectionSchema } from '../types'\r\n\r\nconst fakeSchema: CollectionSchema = {\r\n slug: 'posts',\r\n fields: [],\r\n searchableFields: [],\r\n hasDrafts: false,\r\n} as never\r\n\r\nfunction buildReq() {\r\n return {\r\n payload: {\r\n delete: vi.fn(),\r\n logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },\r\n },\r\n context: {},\r\n user: null,\r\n }\r\n}\r\n\r\ndescribe('deleteDocument', () => {\r\n const schemas = new Map([['posts', fakeSchema]])\r\n\r\n it('deletes via payload.delete with overrideAccess:false and returns success text', async () => {\r\n const tool = createDeleteDocumentTool(schemas)\r\n const req = buildReq()\r\n req.payload.delete.mockResolvedValue({ id: 'a', name: 'Hello' })\r\n\r\n const result = await tool.handler({ collection: 'posts', id: 'a' }, req as never, {})\r\n expect(req.payload.delete).toHaveBeenCalledWith(\r\n expect.objectContaining({\r\n collection: 'posts',\r\n id: 'a',\r\n overrideAccess: false,\r\n }),\r\n )\r\n expect(result.content[0]!.text).toMatch(/Deleted \"Hello\"/)\r\n })\r\n\r\n it('returns text error response when the doc does not exist (no throw)', async () => {\r\n const tool = createDeleteDocumentTool(schemas)\r\n const req = buildReq()\r\n req.payload.delete.mockRejectedValue(new Error('not found'))\r\n\r\n const result = await tool.handler({ collection: 'posts', id: 'missing' }, req as never, {})\r\n expect(result.content[0]!.text).toMatch(/Error deleting missing from posts/)\r\n })\r\n\r\n it('rejects unknown collections at the boundary', async () => {\r\n const tool = createDeleteDocumentTool(schemas)\r\n const req = buildReq()\r\n const result = await tool.handler({ collection: 'unknown', id: 'x' }, req as never, {})\r\n expect(result.content[0]!.text).toMatch(/Unknown collection/)\r\n expect(req.payload.delete).not.toHaveBeenCalled()\r\n })\r\n})\r\n"],"names":["describe","it","expect","vi","createDeleteDocumentTool","fakeSchema","slug","fields","searchableFields","hasDrafts","buildReq","payload","delete","fn","logger","info","warn","error","context","user","schemas","Map","tool","req","mockResolvedValue","id","name","result","handler","collection","toHaveBeenCalledWith","objectContaining","overrideAccess","content","text","toMatch","mockRejectedValue","Error","not","toHaveBeenCalled"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,EAAE,EAAEC,MAAM,EAAEC,EAAE,QAAQ,SAAQ;AACjD,SAASC,wBAAwB,QAAQ,2BAA0B;AAGnE,MAAMC,aAA+B;IACnCC,MAAM;IACNC,QAAQ,EAAE;IACVC,kBAAkB,EAAE;IACpBC,WAAW;AACb;AAEA,SAASC;IACP,OAAO;QACLC,SAAS;YACPC,QAAQT,GAAGU,EAAE;YACbC,QAAQ;gBAAEC,MAAMZ,GAAGU,EAAE;gBAAIG,MAAMb,GAAGU,EAAE;gBAAII,OAAOd,GAAGU,EAAE;YAAG;QACzD;QACAK,SAAS,CAAC;QACVC,MAAM;IACR;AACF;AAEAnB,SAAS,kBAAkB;IACzB,MAAMoB,UAAU,IAAIC,IAAI;QAAC;YAAC;YAAShB;SAAW;KAAC;IAE/CJ,GAAG,iFAAiF;QAClF,MAAMqB,OAAOlB,yBAAyBgB;QACtC,MAAMG,MAAMb;QACZa,IAAIZ,OAAO,CAACC,MAAM,CAACY,iBAAiB,CAAC;YAAEC,IAAI;YAAKC,MAAM;QAAQ;QAE9D,MAAMC,SAAS,MAAML,KAAKM,OAAO,CAAC;YAAEC,YAAY;YAASJ,IAAI;QAAI,GAAGF,KAAc,CAAC;QACnFrB,OAAOqB,IAAIZ,OAAO,CAACC,MAAM,EAAEkB,oBAAoB,CAC7C5B,OAAO6B,gBAAgB,CAAC;YACtBF,YAAY;YACZJ,IAAI;YACJO,gBAAgB;QAClB;QAEF9B,OAAOyB,OAAOM,OAAO,CAAC,EAAE,CAAEC,IAAI,EAAEC,OAAO,CAAC;IAC1C;IAEAlC,GAAG,sEAAsE;QACvE,MAAMqB,OAAOlB,yBAAyBgB;QACtC,MAAMG,MAAMb;QACZa,IAAIZ,OAAO,CAACC,MAAM,CAACwB,iBAAiB,CAAC,IAAIC,MAAM;QAE/C,MAAMV,SAAS,MAAML,KAAKM,OAAO,CAAC;YAAEC,YAAY;YAASJ,IAAI;QAAU,GAAGF,KAAc,CAAC;QACzFrB,OAAOyB,OAAOM,OAAO,CAAC,EAAE,CAAEC,IAAI,EAAEC,OAAO,CAAC;IAC1C;IAEAlC,GAAG,+CAA+C;QAChD,MAAMqB,OAAOlB,yBAAyBgB;QACtC,MAAMG,MAAMb;QACZ,MAAMiB,SAAS,MAAML,KAAKM,OAAO,CAAC;YAAEC,YAAY;YAAWJ,IAAI;QAAI,GAAGF,KAAc,CAAC;QACrFrB,OAAOyB,OAAOM,OAAO,CAAC,EAAE,CAAEC,IAAI,EAAEC,OAAO,CAAC;QACxCjC,OAAOqB,IAAIZ,OAAO,CAACC,MAAM,EAAE0B,GAAG,CAACC,gBAAgB;IACjD;AACF"}
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
const handlerMock = vi.fn(async ()=>new Response('"ok"', {
|
|
3
|
-
status: 200
|
|
4
|
-
}));
|
|
5
|
-
const createMcpHandlerMock = vi.fn(()=>handlerMock);
|
|
6
|
-
vi.mock('mcp-handler', ()=>({
|
|
7
|
-
createMcpHandler: (...args)=>createMcpHandlerMock(...args)
|
|
8
|
-
}));
|
|
9
|
-
import { createMcpEndpoints, MCP_ENDPOINT_PATH } from '../endpoint';
|
|
10
|
-
function buildReq(overrides) {
|
|
11
|
-
const headers = new Headers();
|
|
12
|
-
if (overrides.origin) headers.set('origin', overrides.origin);
|
|
13
|
-
if (overrides.host) headers.set('host', overrides.host);
|
|
14
|
-
const authenticated = overrides.authenticated !== false;
|
|
15
|
-
return {
|
|
16
|
-
url: 'https://app.example.com/api/mcp',
|
|
17
|
-
method: overrides.method ?? 'POST',
|
|
18
|
-
headers,
|
|
19
|
-
body: overrides.body ?? null,
|
|
20
|
-
user: authenticated ? {
|
|
21
|
-
_mcpKey: {
|
|
22
|
-
keyId: 'k1',
|
|
23
|
-
keyPrefix: 'abc12345',
|
|
24
|
-
scopes: null
|
|
25
|
-
}
|
|
26
|
-
} : null
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
beforeEach(()=>{
|
|
30
|
-
handlerMock.mockClear();
|
|
31
|
-
createMcpHandlerMock.mockClear();
|
|
32
|
-
});
|
|
33
|
-
describe('createMcpEndpoints', ()=>{
|
|
34
|
-
const initializeServer = vi.fn();
|
|
35
|
-
const buildInitializeServer = vi.fn(()=>initializeServer);
|
|
36
|
-
beforeEach(()=>{
|
|
37
|
-
initializeServer.mockClear();
|
|
38
|
-
buildInitializeServer.mockClear();
|
|
39
|
-
});
|
|
40
|
-
it('registers POST + GET endpoints at /mcp', ()=>{
|
|
41
|
-
const endpoints = createMcpEndpoints({
|
|
42
|
-
buildInitializeServer
|
|
43
|
-
});
|
|
44
|
-
expect(endpoints).toHaveLength(2);
|
|
45
|
-
const methods = endpoints.map((e)=>e.method).sort();
|
|
46
|
-
expect(methods).toEqual([
|
|
47
|
-
'get',
|
|
48
|
-
'post'
|
|
49
|
-
]);
|
|
50
|
-
for (const e of endpoints)expect(e.path).toBe(MCP_ENDPOINT_PATH);
|
|
51
|
-
});
|
|
52
|
-
it('GET returns 405 with a JSON-RPC error body', async ()=>{
|
|
53
|
-
const [, getEndpoint] = createMcpEndpoints({
|
|
54
|
-
buildInitializeServer
|
|
55
|
-
});
|
|
56
|
-
const res = await getEndpoint.handler(buildReq({
|
|
57
|
-
method: 'GET'
|
|
58
|
-
}));
|
|
59
|
-
expect(res.status).toBe(405);
|
|
60
|
-
const body = await res.json();
|
|
61
|
-
expect(body.jsonrpc).toBe('2.0');
|
|
62
|
-
expect(body.error.message).toMatch(/POST/i);
|
|
63
|
-
});
|
|
64
|
-
it('POST with valid host + no origin delegates to mcp-handler', async ()=>{
|
|
65
|
-
const [postEndpoint] = createMcpEndpoints({
|
|
66
|
-
buildInitializeServer,
|
|
67
|
-
serverURL: 'https://app.example.com'
|
|
68
|
-
});
|
|
69
|
-
const res = await postEndpoint.handler(buildReq({
|
|
70
|
-
host: 'app.example.com'
|
|
71
|
-
}));
|
|
72
|
-
expect(res.status).toBe(200);
|
|
73
|
-
expect(handlerMock).toHaveBeenCalledTimes(1);
|
|
74
|
-
});
|
|
75
|
-
it('rejects POST with mismatched Host header (DNS rebinding mitigation)', async ()=>{
|
|
76
|
-
const [postEndpoint] = createMcpEndpoints({
|
|
77
|
-
buildInitializeServer,
|
|
78
|
-
serverURL: 'https://app.example.com'
|
|
79
|
-
});
|
|
80
|
-
const res = await postEndpoint.handler(buildReq({
|
|
81
|
-
host: 'evil.example.com'
|
|
82
|
-
}));
|
|
83
|
-
expect(res.status).toBe(400);
|
|
84
|
-
const body = await res.json();
|
|
85
|
-
expect(body.error.message).toMatch(/host/i);
|
|
86
|
-
expect(handlerMock).not.toHaveBeenCalled();
|
|
87
|
-
});
|
|
88
|
-
it('skips host validation when serverURL is not configured', async ()=>{
|
|
89
|
-
const [postEndpoint] = createMcpEndpoints({
|
|
90
|
-
buildInitializeServer
|
|
91
|
-
});
|
|
92
|
-
const res = await postEndpoint.handler(buildReq({
|
|
93
|
-
host: 'whatever.example.com'
|
|
94
|
-
}));
|
|
95
|
-
expect(res.status).toBe(200);
|
|
96
|
-
});
|
|
97
|
-
it('rejects POST with disallowed Origin', async ()=>{
|
|
98
|
-
const [postEndpoint] = createMcpEndpoints({
|
|
99
|
-
buildInitializeServer,
|
|
100
|
-
allowedOrigins: [
|
|
101
|
-
'https://app.example.com'
|
|
102
|
-
]
|
|
103
|
-
});
|
|
104
|
-
const res = await postEndpoint.handler(buildReq({
|
|
105
|
-
origin: 'https://attacker.example.com'
|
|
106
|
-
}));
|
|
107
|
-
expect(res.status).toBe(403);
|
|
108
|
-
const body = await res.json();
|
|
109
|
-
expect(body.error.message).toMatch(/origin/i);
|
|
110
|
-
expect(handlerMock).not.toHaveBeenCalled();
|
|
111
|
-
});
|
|
112
|
-
it('allows server-to-server POST (no Origin header) even when allowedOrigins is empty', async ()=>{
|
|
113
|
-
const [postEndpoint] = createMcpEndpoints({
|
|
114
|
-
buildInitializeServer
|
|
115
|
-
});
|
|
116
|
-
const res = await postEndpoint.handler(buildReq({}));
|
|
117
|
-
expect(res.status).toBe(200);
|
|
118
|
-
});
|
|
119
|
-
it('forwards a constructed Fetch Request to the mcp-handler', async ()=>{
|
|
120
|
-
const [postEndpoint] = createMcpEndpoints({
|
|
121
|
-
buildInitializeServer
|
|
122
|
-
});
|
|
123
|
-
await postEndpoint.handler(buildReq({}));
|
|
124
|
-
const [forwarded] = handlerMock.mock.calls[0];
|
|
125
|
-
expect(forwarded).toBeInstanceOf(Request);
|
|
126
|
-
expect(forwarded.method).toBe('POST');
|
|
127
|
-
});
|
|
128
|
-
it('rejects unauthenticated POSTs with 401 before constructing the mcp-handler', async ()=>{
|
|
129
|
-
const [postEndpoint] = createMcpEndpoints({
|
|
130
|
-
buildInitializeServer
|
|
131
|
-
});
|
|
132
|
-
const res = await postEndpoint.handler(buildReq({
|
|
133
|
-
authenticated: false
|
|
134
|
-
}));
|
|
135
|
-
expect(res.status).toBe(401);
|
|
136
|
-
const body = await res.json();
|
|
137
|
-
expect(body.error.message).toMatch(/MCP API key required/);
|
|
138
|
-
expect(handlerMock).not.toHaveBeenCalled();
|
|
139
|
-
expect(buildInitializeServer).not.toHaveBeenCalled();
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
//# sourceMappingURL=endpoint.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/__tests__/endpoint.test.ts"],"sourcesContent":["import { describe, it, expect, vi, beforeEach } from 'vitest'\r\n\r\nconst handlerMock = vi.fn(async () => new Response('\"ok\"', { status: 200 }))\r\nconst createMcpHandlerMock = vi.fn(() => handlerMock)\r\n\r\nvi.mock('mcp-handler', () => ({\r\n createMcpHandler: (...args: unknown[]) => createMcpHandlerMock(...args),\r\n}))\r\n\r\nimport { createMcpEndpoints, MCP_ENDPOINT_PATH } from '../endpoint'\r\n\r\nfunction buildReq(overrides: {\r\n origin?: string | null\r\n host?: string | null\r\n method?: string\r\n body?: unknown\r\n authenticated?: boolean\r\n}) {\r\n const headers = new Headers()\r\n if (overrides.origin) headers.set('origin', overrides.origin)\r\n if (overrides.host) headers.set('host', overrides.host)\r\n const authenticated = overrides.authenticated !== false\r\n return {\r\n url: 'https://app.example.com/api/mcp',\r\n method: overrides.method ?? 'POST',\r\n headers,\r\n body: overrides.body ?? null,\r\n user: authenticated\r\n ? {\r\n _mcpKey: { keyId: 'k1', keyPrefix: 'abc12345', scopes: null },\r\n }\r\n : null,\r\n }\r\n}\r\n\r\nbeforeEach(() => {\r\n handlerMock.mockClear()\r\n createMcpHandlerMock.mockClear()\r\n})\r\n\r\ndescribe('createMcpEndpoints', () => {\r\n const initializeServer = vi.fn()\r\n const buildInitializeServer = vi.fn(() => initializeServer)\r\n\r\n beforeEach(() => {\r\n initializeServer.mockClear()\r\n buildInitializeServer.mockClear()\r\n })\r\n\r\n it('registers POST + GET endpoints at /mcp', () => {\r\n const endpoints = createMcpEndpoints({ buildInitializeServer })\r\n expect(endpoints).toHaveLength(2)\r\n const methods = endpoints.map((e) => e.method).sort()\r\n expect(methods).toEqual(['get', 'post'])\r\n for (const e of endpoints) expect(e.path).toBe(MCP_ENDPOINT_PATH)\r\n })\r\n\r\n it('GET returns 405 with a JSON-RPC error body', async () => {\r\n const [, getEndpoint] = createMcpEndpoints({ buildInitializeServer })\r\n const res = await getEndpoint.handler(buildReq({ method: 'GET' }) as never)\r\n expect(res.status).toBe(405)\r\n const body = (await res.json()) as { jsonrpc: string; error: { message: string } }\r\n expect(body.jsonrpc).toBe('2.0')\r\n expect(body.error.message).toMatch(/POST/i)\r\n })\r\n\r\n it('POST with valid host + no origin delegates to mcp-handler', async () => {\r\n const [postEndpoint] = createMcpEndpoints({\r\n buildInitializeServer,\r\n serverURL: 'https://app.example.com',\r\n })\r\n const res = await postEndpoint.handler(\r\n buildReq({ host: 'app.example.com' }) as never,\r\n )\r\n expect(res.status).toBe(200)\r\n expect(handlerMock).toHaveBeenCalledTimes(1)\r\n })\r\n\r\n it('rejects POST with mismatched Host header (DNS rebinding mitigation)', async () => {\r\n const [postEndpoint] = createMcpEndpoints({\r\n buildInitializeServer,\r\n serverURL: 'https://app.example.com',\r\n })\r\n const res = await postEndpoint.handler(\r\n buildReq({ host: 'evil.example.com' }) as never,\r\n )\r\n expect(res.status).toBe(400)\r\n const body = (await res.json()) as { error: { message: string } }\r\n expect(body.error.message).toMatch(/host/i)\r\n expect(handlerMock).not.toHaveBeenCalled()\r\n })\r\n\r\n it('skips host validation when serverURL is not configured', async () => {\r\n const [postEndpoint] = createMcpEndpoints({ buildInitializeServer })\r\n const res = await postEndpoint.handler(\r\n buildReq({ host: 'whatever.example.com' }) as never,\r\n )\r\n expect(res.status).toBe(200)\r\n })\r\n\r\n it('rejects POST with disallowed Origin', async () => {\r\n const [postEndpoint] = createMcpEndpoints({\r\n buildInitializeServer,\r\n allowedOrigins: ['https://app.example.com'],\r\n })\r\n const res = await postEndpoint.handler(\r\n buildReq({ origin: 'https://attacker.example.com' }) as never,\r\n )\r\n expect(res.status).toBe(403)\r\n const body = (await res.json()) as { error: { message: string } }\r\n expect(body.error.message).toMatch(/origin/i)\r\n expect(handlerMock).not.toHaveBeenCalled()\r\n })\r\n\r\n it('allows server-to-server POST (no Origin header) even when allowedOrigins is empty', async () => {\r\n const [postEndpoint] = createMcpEndpoints({ buildInitializeServer })\r\n const res = await postEndpoint.handler(buildReq({}) as never)\r\n expect(res.status).toBe(200)\r\n })\r\n\r\n it('forwards a constructed Fetch Request to the mcp-handler', async () => {\r\n const [postEndpoint] = createMcpEndpoints({ buildInitializeServer })\r\n await postEndpoint.handler(buildReq({}) as never)\r\n const [forwarded] = handlerMock.mock.calls[0]!\r\n expect(forwarded).toBeInstanceOf(Request)\r\n expect((forwarded as Request).method).toBe('POST')\r\n })\r\n\r\n it('rejects unauthenticated POSTs with 401 before constructing the mcp-handler', async () => {\r\n const [postEndpoint] = createMcpEndpoints({ buildInitializeServer })\r\n const res = await postEndpoint.handler(buildReq({ authenticated: false }) as never)\r\n expect(res.status).toBe(401)\r\n const body = (await res.json()) as { error: { message: string } }\r\n expect(body.error.message).toMatch(/MCP API key required/)\r\n expect(handlerMock).not.toHaveBeenCalled()\r\n expect(buildInitializeServer).not.toHaveBeenCalled()\r\n })\r\n})\r\n"],"names":["describe","it","expect","vi","beforeEach","handlerMock","fn","Response","status","createMcpHandlerMock","mock","createMcpHandler","args","createMcpEndpoints","MCP_ENDPOINT_PATH","buildReq","overrides","headers","Headers","origin","set","host","authenticated","url","method","body","user","_mcpKey","keyId","keyPrefix","scopes","mockClear","initializeServer","buildInitializeServer","endpoints","toHaveLength","methods","map","e","sort","toEqual","path","toBe","getEndpoint","res","handler","json","jsonrpc","error","message","toMatch","postEndpoint","serverURL","toHaveBeenCalledTimes","not","toHaveBeenCalled","allowedOrigins","forwarded","calls","toBeInstanceOf","Request"],"mappings":"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"}
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
-
import { createFindDocumentTool } from '../tools/find-document';
|
|
3
|
-
const fakeSchema = {
|
|
4
|
-
slug: 'posts',
|
|
5
|
-
fields: [],
|
|
6
|
-
searchableFields: [],
|
|
7
|
-
hasDrafts: true
|
|
8
|
-
};
|
|
9
|
-
const draftCollectionConfig = {
|
|
10
|
-
slug: 'posts',
|
|
11
|
-
admin: {
|
|
12
|
-
livePreview: {
|
|
13
|
-
url: ({ data })=>`/preview/posts/${data.slug ?? data.id ?? ''}`
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
|
-
fields: [],
|
|
17
|
-
versions: {
|
|
18
|
-
drafts: true
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
function buildReq() {
|
|
22
|
-
return {
|
|
23
|
-
payload: {
|
|
24
|
-
find: vi.fn(),
|
|
25
|
-
findByID: vi.fn(),
|
|
26
|
-
logger: {
|
|
27
|
-
info: vi.fn(),
|
|
28
|
-
warn: vi.fn(),
|
|
29
|
-
error: vi.fn()
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
context: {},
|
|
33
|
-
user: null
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
describe('findDocument', ()=>{
|
|
37
|
-
const schemas = new Map([
|
|
38
|
-
[
|
|
39
|
-
'posts',
|
|
40
|
-
fakeSchema
|
|
41
|
-
]
|
|
42
|
-
]);
|
|
43
|
-
const drafts = new Set([
|
|
44
|
-
'posts'
|
|
45
|
-
]);
|
|
46
|
-
const configs = new Map([
|
|
47
|
-
[
|
|
48
|
-
'posts',
|
|
49
|
-
draftCollectionConfig
|
|
50
|
-
]
|
|
51
|
-
]);
|
|
52
|
-
it('calls findByID with overrideAccess:false when id is set', async ()=>{
|
|
53
|
-
const tool = createFindDocumentTool(schemas, drafts, configs, 'https://app.example.com');
|
|
54
|
-
const req = buildReq();
|
|
55
|
-
req.payload.findByID.mockResolvedValue({
|
|
56
|
-
id: 'a',
|
|
57
|
-
_status: 'published'
|
|
58
|
-
});
|
|
59
|
-
const result = await tool.handler({
|
|
60
|
-
collection: 'posts',
|
|
61
|
-
id: 'a'
|
|
62
|
-
}, req, {});
|
|
63
|
-
expect(req.payload.findByID).toHaveBeenCalledWith(expect.objectContaining({
|
|
64
|
-
collection: 'posts',
|
|
65
|
-
id: 'a',
|
|
66
|
-
overrideAccess: false
|
|
67
|
-
}));
|
|
68
|
-
expect(result.content[0].text).toContain('"id":"a"');
|
|
69
|
-
});
|
|
70
|
-
it('parses the where JSON string and forwards it to find()', async ()=>{
|
|
71
|
-
const tool = createFindDocumentTool(schemas, drafts, configs, undefined);
|
|
72
|
-
const req = buildReq();
|
|
73
|
-
req.payload.find.mockResolvedValue({
|
|
74
|
-
docs: [],
|
|
75
|
-
totalDocs: 0
|
|
76
|
-
});
|
|
77
|
-
await tool.handler({
|
|
78
|
-
collection: 'posts',
|
|
79
|
-
where: '{"status":{"equals":"published"}}',
|
|
80
|
-
limit: 10
|
|
81
|
-
}, req, {});
|
|
82
|
-
expect(req.payload.find).toHaveBeenCalledWith(expect.objectContaining({
|
|
83
|
-
collection: 'posts',
|
|
84
|
-
where: {
|
|
85
|
-
status: {
|
|
86
|
-
equals: 'published'
|
|
87
|
-
}
|
|
88
|
-
},
|
|
89
|
-
limit: 10,
|
|
90
|
-
overrideAccess: false
|
|
91
|
-
}));
|
|
92
|
-
});
|
|
93
|
-
it('returns a text error response (no exception) on invalid where JSON', async ()=>{
|
|
94
|
-
const tool = createFindDocumentTool(schemas, drafts, configs, undefined);
|
|
95
|
-
const req = buildReq();
|
|
96
|
-
const result = await tool.handler({
|
|
97
|
-
collection: 'posts',
|
|
98
|
-
where: 'not-json'
|
|
99
|
-
}, req, {});
|
|
100
|
-
expect(result.content[0].text).toMatch(/valid JSON/);
|
|
101
|
-
expect(req.payload.find).not.toHaveBeenCalled();
|
|
102
|
-
});
|
|
103
|
-
it('returns a text error response when collection is not registered', async ()=>{
|
|
104
|
-
const tool = createFindDocumentTool(schemas, drafts, configs, undefined);
|
|
105
|
-
const req = buildReq();
|
|
106
|
-
const result = await tool.handler({
|
|
107
|
-
collection: 'unknown'
|
|
108
|
-
}, req, {});
|
|
109
|
-
expect(result.content[0].text).toMatch(/Unknown collection/);
|
|
110
|
-
});
|
|
111
|
-
it('appends a preview URL when a draft document is returned by id lookup', async ()=>{
|
|
112
|
-
const tool = createFindDocumentTool(schemas, drafts, configs, 'https://app.example.com');
|
|
113
|
-
const req = buildReq();
|
|
114
|
-
req.payload.findByID.mockResolvedValue({
|
|
115
|
-
id: 'a',
|
|
116
|
-
slug: 'hello',
|
|
117
|
-
_status: 'draft'
|
|
118
|
-
});
|
|
119
|
-
const result = await tool.handler({
|
|
120
|
-
collection: 'posts',
|
|
121
|
-
id: 'a'
|
|
122
|
-
}, req, {});
|
|
123
|
-
const allText = result.content.map((c)=>c.text).join('\n');
|
|
124
|
-
expect(allText).toMatch(/draft/i);
|
|
125
|
-
expect(allText).toContain('https://app.example.com/preview/posts/hello');
|
|
126
|
-
});
|
|
127
|
-
it('falls back to the admin-panel hint when the collection has no preview function', async ()=>{
|
|
128
|
-
const noPreviewConfig = {
|
|
129
|
-
slug: 'posts',
|
|
130
|
-
fields: []
|
|
131
|
-
};
|
|
132
|
-
const tool = createFindDocumentTool(schemas, drafts, new Map([
|
|
133
|
-
[
|
|
134
|
-
'posts',
|
|
135
|
-
noPreviewConfig
|
|
136
|
-
]
|
|
137
|
-
]), undefined);
|
|
138
|
-
const req = buildReq();
|
|
139
|
-
req.payload.findByID.mockResolvedValue({
|
|
140
|
-
id: 'a',
|
|
141
|
-
_status: 'draft'
|
|
142
|
-
});
|
|
143
|
-
const result = await tool.handler({
|
|
144
|
-
collection: 'posts',
|
|
145
|
-
id: 'a'
|
|
146
|
-
}, req, {});
|
|
147
|
-
const allText = result.content.map((c)=>c.text).join('\n');
|
|
148
|
-
expect(allText).toMatch(/admin panel/i);
|
|
149
|
-
});
|
|
150
|
-
it('omits preview decoration entirely when previewDisabled is true', async ()=>{
|
|
151
|
-
const tool = createFindDocumentTool(schemas, drafts, configs, 'https://app.example.com', true);
|
|
152
|
-
const req = buildReq();
|
|
153
|
-
req.payload.findByID.mockResolvedValue({
|
|
154
|
-
id: 'a',
|
|
155
|
-
slug: 'hello',
|
|
156
|
-
_status: 'draft'
|
|
157
|
-
});
|
|
158
|
-
const result = await tool.handler({
|
|
159
|
-
collection: 'posts',
|
|
160
|
-
id: 'a'
|
|
161
|
-
}, req, {});
|
|
162
|
-
expect(result.content).toHaveLength(1);
|
|
163
|
-
const decoration = result.content.slice(1);
|
|
164
|
-
expect(decoration).toHaveLength(0);
|
|
165
|
-
});
|
|
166
|
-
it('catches errors and returns text error response, no exception', async ()=>{
|
|
167
|
-
const tool = createFindDocumentTool(schemas, drafts, configs, undefined);
|
|
168
|
-
const req = buildReq();
|
|
169
|
-
req.payload.findByID.mockRejectedValue(new Error('not found'));
|
|
170
|
-
const result = await tool.handler({
|
|
171
|
-
collection: 'posts',
|
|
172
|
-
id: 'a'
|
|
173
|
-
}, req, {});
|
|
174
|
-
expect(result.content[0].text).toMatch(/Error reading from posts/);
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
//# sourceMappingURL=find-document.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/__tests__/find-document.test.ts"],"sourcesContent":["import { describe, it, expect, vi } from 'vitest'\r\nimport type { CollectionConfig } from 'payload'\r\nimport { createFindDocumentTool } from '../tools/find-document'\r\nimport type { CollectionSchema } from '../types'\r\n\r\nconst fakeSchema: CollectionSchema = {\r\n slug: 'posts',\r\n fields: [],\r\n searchableFields: [],\r\n hasDrafts: true,\r\n} as never\r\n\r\nconst draftCollectionConfig: CollectionConfig = {\r\n slug: 'posts',\r\n admin: {\r\n livePreview: {\r\n url: ({ data }: { data: Record<string, unknown> }) =>\r\n `/preview/posts/${data.slug ?? data.id ?? ''}`,\r\n },\r\n } as never,\r\n fields: [],\r\n versions: { drafts: true },\r\n}\r\n\r\nfunction buildReq() {\r\n return {\r\n payload: {\r\n find: vi.fn(),\r\n findByID: vi.fn(),\r\n logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },\r\n },\r\n context: {},\r\n user: null,\r\n }\r\n}\r\n\r\ndescribe('findDocument', () => {\r\n const schemas = new Map([['posts', fakeSchema]])\r\n const drafts = new Set(['posts'])\r\n const configs = new Map([['posts', draftCollectionConfig]])\r\n\r\n it('calls findByID with overrideAccess:false when id is set', async () => {\r\n const tool = createFindDocumentTool(schemas, drafts, configs, 'https://app.example.com')\r\n const req = buildReq()\r\n req.payload.findByID.mockResolvedValue({ id: 'a', _status: 'published' })\r\n\r\n const result = await tool.handler({ collection: 'posts', id: 'a' }, req as never, {})\r\n expect(req.payload.findByID).toHaveBeenCalledWith(\r\n expect.objectContaining({\r\n collection: 'posts',\r\n id: 'a',\r\n overrideAccess: false,\r\n }),\r\n )\r\n expect(result.content[0]!.text).toContain('\"id\":\"a\"')\r\n })\r\n\r\n it('parses the where JSON string and forwards it to find()', async () => {\r\n const tool = createFindDocumentTool(schemas, drafts, configs, undefined)\r\n const req = buildReq()\r\n req.payload.find.mockResolvedValue({ docs: [], totalDocs: 0 })\r\n\r\n await tool.handler(\r\n { collection: 'posts', where: '{\"status\":{\"equals\":\"published\"}}', limit: 10 },\r\n req as never,\r\n {},\r\n )\r\n expect(req.payload.find).toHaveBeenCalledWith(\r\n expect.objectContaining({\r\n collection: 'posts',\r\n where: { status: { equals: 'published' } },\r\n limit: 10,\r\n overrideAccess: false,\r\n }),\r\n )\r\n })\r\n\r\n it('returns a text error response (no exception) on invalid where JSON', async () => {\r\n const tool = createFindDocumentTool(schemas, drafts, configs, undefined)\r\n const req = buildReq()\r\n\r\n const result = await tool.handler(\r\n { collection: 'posts', where: 'not-json' },\r\n req as never,\r\n {},\r\n )\r\n expect(result.content[0]!.text).toMatch(/valid JSON/)\r\n expect(req.payload.find).not.toHaveBeenCalled()\r\n })\r\n\r\n it('returns a text error response when collection is not registered', async () => {\r\n const tool = createFindDocumentTool(schemas, drafts, configs, undefined)\r\n const req = buildReq()\r\n const result = await tool.handler({ collection: 'unknown' }, req as never, {})\r\n expect(result.content[0]!.text).toMatch(/Unknown collection/)\r\n })\r\n\r\n it('appends a preview URL when a draft document is returned by id lookup', async () => {\r\n const tool = createFindDocumentTool(schemas, drafts, configs, 'https://app.example.com')\r\n const req = buildReq()\r\n req.payload.findByID.mockResolvedValue({ id: 'a', slug: 'hello', _status: 'draft' })\r\n\r\n const result = await tool.handler({ collection: 'posts', id: 'a' }, req as never, {})\r\n const allText = result.content.map((c) => c.text).join('\\n')\r\n expect(allText).toMatch(/draft/i)\r\n expect(allText).toContain('https://app.example.com/preview/posts/hello')\r\n })\r\n\r\n it('falls back to the admin-panel hint when the collection has no preview function', async () => {\r\n const noPreviewConfig: CollectionConfig = { slug: 'posts', fields: [] }\r\n const tool = createFindDocumentTool(\r\n schemas,\r\n drafts,\r\n new Map([['posts', noPreviewConfig]]),\r\n undefined,\r\n )\r\n const req = buildReq()\r\n req.payload.findByID.mockResolvedValue({ id: 'a', _status: 'draft' })\r\n\r\n const result = await tool.handler({ collection: 'posts', id: 'a' }, req as never, {})\r\n const allText = result.content.map((c) => c.text).join('\\n')\r\n expect(allText).toMatch(/admin panel/i)\r\n })\r\n\r\n it('omits preview decoration entirely when previewDisabled is true', async () => {\r\n const tool = createFindDocumentTool(\r\n schemas,\r\n drafts,\r\n configs,\r\n 'https://app.example.com',\r\n true,\r\n )\r\n const req = buildReq()\r\n req.payload.findByID.mockResolvedValue({ id: 'a', slug: 'hello', _status: 'draft' })\r\n const result = await tool.handler({ collection: 'posts', id: 'a' }, req as never, {})\r\n expect(result.content).toHaveLength(1)\r\n const decoration = result.content.slice(1)\r\n expect(decoration).toHaveLength(0)\r\n })\r\n\r\n it('catches errors and returns text error response, no exception', async () => {\r\n const tool = createFindDocumentTool(schemas, drafts, configs, undefined)\r\n const req = buildReq()\r\n req.payload.findByID.mockRejectedValue(new Error('not found'))\r\n const result = await tool.handler({ collection: 'posts', id: 'a' }, req as never, {})\r\n expect(result.content[0]!.text).toMatch(/Error reading from posts/)\r\n })\r\n})\r\n"],"names":["describe","it","expect","vi","createFindDocumentTool","fakeSchema","slug","fields","searchableFields","hasDrafts","draftCollectionConfig","admin","livePreview","url","data","id","versions","drafts","buildReq","payload","find","fn","findByID","logger","info","warn","error","context","user","schemas","Map","Set","configs","tool","req","mockResolvedValue","_status","result","handler","collection","toHaveBeenCalledWith","objectContaining","overrideAccess","content","text","toContain","undefined","docs","totalDocs","where","limit","status","equals","toMatch","not","toHaveBeenCalled","allText","map","c","join","noPreviewConfig","toHaveLength","decoration","slice","mockRejectedValue","Error"],"mappings":"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"}
|