@forumone/throughline-core 0.9.0 → 0.10.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.
Files changed (65) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/README.md +192 -33
  3. package/bin/payload-cli.mjs +443 -0
  4. package/bin/throughline-payload.mjs +6 -0
  5. package/dist/audit/collection.d.ts +3 -0
  6. package/dist/audit/collection.d.ts.map +1 -1
  7. package/dist/audit/collection.js +2 -0
  8. package/dist/audit/collection.js.map +1 -1
  9. package/dist/env/index.d.ts +54 -0
  10. package/dist/env/index.d.ts.map +1 -0
  11. package/dist/env/index.js +113 -0
  12. package/dist/env/index.js.map +1 -0
  13. package/dist/index.d.ts +6 -2
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +3 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/mcp/api-key-access.d.ts +50 -0
  18. package/dist/mcp/api-key-access.d.ts.map +1 -0
  19. package/dist/mcp/api-key-access.js +94 -0
  20. package/dist/mcp/api-key-access.js.map +1 -0
  21. package/dist/mcp/envelope.d.ts +6 -0
  22. package/dist/mcp/envelope.d.ts.map +1 -1
  23. package/dist/mcp/envelope.js +6 -0
  24. package/dist/mcp/envelope.js.map +1 -1
  25. package/dist/mcp/index.d.ts +1 -0
  26. package/dist/mcp/index.d.ts.map +1 -1
  27. package/dist/mcp/index.js +1 -0
  28. package/dist/mcp/index.js.map +1 -1
  29. package/dist/mcp/payload-mcp.d.ts +1 -15
  30. package/dist/mcp/payload-mcp.d.ts.map +1 -1
  31. package/dist/mcp/payload-mcp.js +40 -1
  32. package/dist/mcp/payload-mcp.js.map +1 -1
  33. package/dist/observability/collection.d.ts +13 -0
  34. package/dist/observability/collection.d.ts.map +1 -0
  35. package/dist/observability/collection.js +76 -0
  36. package/dist/observability/collection.js.map +1 -0
  37. package/dist/observability/index.d.ts +9 -0
  38. package/dist/observability/index.d.ts.map +1 -0
  39. package/dist/observability/index.js +5 -0
  40. package/dist/observability/index.js.map +1 -0
  41. package/dist/observability/plugin.d.ts +21 -0
  42. package/dist/observability/plugin.d.ts.map +1 -0
  43. package/dist/observability/plugin.js +51 -0
  44. package/dist/observability/plugin.js.map +1 -0
  45. package/dist/observability/report.d.ts +175 -0
  46. package/dist/observability/report.d.ts.map +1 -0
  47. package/dist/observability/report.js +274 -0
  48. package/dist/observability/report.js.map +1 -0
  49. package/dist/observability/writer.d.ts +24 -0
  50. package/dist/observability/writer.d.ts.map +1 -0
  51. package/dist/observability/writer.js +72 -0
  52. package/dist/observability/writer.js.map +1 -0
  53. package/dist/testing/anonymousAccess.d.ts +111 -0
  54. package/dist/testing/anonymousAccess.d.ts.map +1 -0
  55. package/dist/testing/anonymousAccess.js +185 -0
  56. package/dist/testing/anonymousAccess.js.map +1 -0
  57. package/dist/testing/describeAnonymousAccess.d.ts +26 -0
  58. package/dist/testing/describeAnonymousAccess.d.ts.map +1 -0
  59. package/dist/testing/describeAnonymousAccess.js +76 -0
  60. package/dist/testing/describeAnonymousAccess.js.map +1 -0
  61. package/dist/testing/index.d.ts +11 -0
  62. package/dist/testing/index.d.ts.map +1 -0
  63. package/dist/testing/index.js +9 -0
  64. package/dist/testing/index.js.map +1 -0
  65. package/package.json +30 -7
package/dist/index.d.ts CHANGED
@@ -1,11 +1,15 @@
1
1
  export { AUDIT_ACTIONS, AUDIT_MCP_SERVERS, DEFAULT_AUDIT_SLUG, auditPlugin, createAuditCollection, createAuditWriter, getAuditWriter, } from './audit/index.js';
2
2
  export type { AuditAction, AuditActor, AuditCollectionOptions, AuditEventInput, AuditMcpServer, AuditPluginOptions, AuditWriter, AuditWriterOptions, } from './audit/index.js';
3
+ export { EnvironmentError, assertEnvironment, checkEnvValue } from './env/index.js';
4
+ export type { EnvValueProblem, EnvironmentCheck } from './env/index.js';
3
5
  export { createInngestClient } from './events/index.js';
4
6
  export type { CoreEvents, FrameworkEvents, InngestClientOptions } from './events/index.js';
5
- export { McpMetaSchema, auditContext, deniedEnvelope, createMcpToolCollector, toPayloadMcpTool, toPayloadMcpTools, withMeta, } from './mcp/index.js';
7
+ export { MCP_API_KEYS_SLUG, McpMetaSchema, auditContext, deniedEnvelope, createMcpToolCollector, isMcpApiKeyPrincipal, isSignedIn, mcpApiKeyAccess, signedIn, toPayloadMcpTool, toPayloadMcpTools, withMeta, } from './mcp/index.js';
6
8
  export type { AddToolsOptions, AuditContextFields, CreateMcpToolCollectorOptions, DeclareToolsOptions, McpMeta, McpToolCollector, McpToolDescriptor, PayloadMcpRequest, PayloadMcpTool, ToPayloadMcpToolOptions, } from './mcp/index.js';
7
9
  export { defaultLogger, createNamedLogger } from './logger/index.js';
10
+ export { DEFAULT_JOB_FAILURES_SLUG, DEFAULT_REPORTED_HEADERS, ERROR_WEBHOOK_URL_ENV, JOB_FAILURE_KINDS, NEVER_REPORTED_HEADERS, buildHealthcheckFailureReport, buildJobFailureReport, buildRequestErrorReport, clientPrefix, createErrorReporter, createJobFailureWriter, createJobFailuresCollection, currentDeployment, describeErrorReporting, getJobFailureWriter, jobFailuresPlugin, reportError, summariseReport, } from './observability/index.js';
11
+ export type { BuildJobFailureReportInput, BuildRequestErrorReportOptions, ErrorReport, ErrorReporter, ErrorReporterOptions, HealthcheckFailureReport, JobFailureKind, JobFailureReport, JobFailureWriter, JobFailureWriterOptions, JobFailuresCollectionOptions, JobFailuresPluginOptions, ReportDeployment, ReportableHeaders, RequestErrorReport, } from './observability/index.js';
8
12
  export { documentContentHash, formatZodIssues, sha256Hex, unwrapRelationshipId, } from './utils/index.js';
9
13
  export type { DocumentContentHashOptions } from './utils/index.js';
10
- export type { AuthenticatedUser, BaseCorePluginOptions, CorePlugin, Logger, McpToolContext, McpToolDefinition, PluginRegistry, PluginRegistryEntry, } from '@forumone/throughline-plugin-contract';
14
+ export type { AuthenticatedUser, BaseCorePluginOptions, CollectionPluginOptions, CorePlugin, EnvRequirement, Logger, McpToolContext, McpToolDefinition, PluginAdminGroup, PluginAdminOptions, PluginRegistry, PluginRegistryEntry, } from '@forumone/throughline-plugin-contract';
11
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,GACf,MAAM,kBAAkB,CAAA;AACzB,YAAY,EACV,WAAW,EACX,UAAU,EACV,sBAAsB,EACtB,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,kBAAkB,GACnB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACvD,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAE1F,OAAO,EACL,aAAa,EACb,YAAY,EACZ,cAAc,EACd,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EACjB,QAAQ,GACT,MAAM,gBAAgB,CAAA;AACvB,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,6BAA6B,EAC7B,mBAAmB,EACnB,OAAO,EACP,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,uBAAuB,GACxB,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAEpE,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,SAAS,EACT,oBAAoB,GACrB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAA;AAIlE,YAAY,EACV,iBAAiB,EACjB,qBAAqB,EACrB,UAAU,EACV,MAAM,EACN,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,mBAAmB,GACpB,MAAM,uCAAuC,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,GACf,MAAM,kBAAkB,CAAA;AACzB,YAAY,EACV,WAAW,EACX,UAAU,EACV,sBAAsB,EACtB,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,kBAAkB,GACnB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AACnF,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAEvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACvD,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAE1F,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,YAAY,EACZ,cAAc,EACd,sBAAsB,EACtB,oBAAoB,EACpB,UAAU,EACV,eAAe,EACf,QAAQ,EACR,gBAAgB,EAChB,iBAAiB,EACjB,QAAQ,GACT,MAAM,gBAAgB,CAAA;AACvB,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,6BAA6B,EAC7B,mBAAmB,EACnB,OAAO,EACP,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,uBAAuB,GACxB,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAEpE,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,qBAAqB,EACrB,iBAAiB,EACjB,sBAAsB,EACtB,6BAA6B,EAC7B,qBAAqB,EACrB,uBAAuB,EACvB,YAAY,EACZ,mBAAmB,EACnB,sBAAsB,EACtB,2BAA2B,EAC3B,iBAAiB,EACjB,sBAAsB,EACtB,mBAAmB,EACnB,iBAAiB,EACjB,WAAW,EACX,eAAe,GAChB,MAAM,0BAA0B,CAAA;AACjC,YAAY,EACV,0BAA0B,EAC1B,8BAA8B,EAC9B,WAAW,EACX,aAAa,EACb,oBAAoB,EACpB,wBAAwB,EACxB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,uBAAuB,EACvB,4BAA4B,EAC5B,wBAAwB,EACxB,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,0BAA0B,CAAA;AAEjC,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,SAAS,EACT,oBAAoB,GACrB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAA;AAIlE,YAAY,EACV,iBAAiB,EACjB,qBAAqB,EACrB,uBAAuB,EACvB,UAAU,EACV,cAAc,EACd,MAAM,EACN,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACd,mBAAmB,GACpB,MAAM,uCAAuC,CAAA"}
package/dist/index.js CHANGED
@@ -1,8 +1,10 @@
1
1
  // Subsystems are re-exported as they are added during C4. The aggregated
2
2
  // surface here mirrors the package's `./<subpath>` exports.
3
3
  export { AUDIT_ACTIONS, AUDIT_MCP_SERVERS, DEFAULT_AUDIT_SLUG, auditPlugin, createAuditCollection, createAuditWriter, getAuditWriter, } from './audit/index.js';
4
+ export { EnvironmentError, assertEnvironment, checkEnvValue } from './env/index.js';
4
5
  export { createInngestClient } from './events/index.js';
5
- export { McpMetaSchema, auditContext, deniedEnvelope, createMcpToolCollector, toPayloadMcpTool, toPayloadMcpTools, withMeta, } from './mcp/index.js';
6
+ export { MCP_API_KEYS_SLUG, McpMetaSchema, auditContext, deniedEnvelope, createMcpToolCollector, isMcpApiKeyPrincipal, isSignedIn, mcpApiKeyAccess, signedIn, toPayloadMcpTool, toPayloadMcpTools, withMeta, } from './mcp/index.js';
6
7
  export { defaultLogger, createNamedLogger } from './logger/index.js';
8
+ export { DEFAULT_JOB_FAILURES_SLUG, DEFAULT_REPORTED_HEADERS, ERROR_WEBHOOK_URL_ENV, JOB_FAILURE_KINDS, NEVER_REPORTED_HEADERS, buildHealthcheckFailureReport, buildJobFailureReport, buildRequestErrorReport, clientPrefix, createErrorReporter, createJobFailureWriter, createJobFailuresCollection, currentDeployment, describeErrorReporting, getJobFailureWriter, jobFailuresPlugin, reportError, summariseReport, } from './observability/index.js';
7
9
  export { documentContentHash, formatZodIssues, sha256Hex, unwrapRelationshipId, } from './utils/index.js';
8
10
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,4DAA4D;AAE5D,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,GACf,MAAM,kBAAkB,CAAA;AAYzB,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAGvD,OAAO,EACL,aAAa,EACb,YAAY,EACZ,cAAc,EACd,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EACjB,QAAQ,GACT,MAAM,gBAAgB,CAAA;AAcvB,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAEpE,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,SAAS,EACT,oBAAoB,GACrB,MAAM,kBAAkB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,4DAA4D;AAE5D,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,GACf,MAAM,kBAAkB,CAAA;AAYzB,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAGnF,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAGvD,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,YAAY,EACZ,cAAc,EACd,sBAAsB,EACtB,oBAAoB,EACpB,UAAU,EACV,eAAe,EACf,QAAQ,EACR,gBAAgB,EAChB,iBAAiB,EACjB,QAAQ,GACT,MAAM,gBAAgB,CAAA;AAcvB,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAEpE,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,qBAAqB,EACrB,iBAAiB,EACjB,sBAAsB,EACtB,6BAA6B,EAC7B,qBAAqB,EACrB,uBAAuB,EACvB,YAAY,EACZ,mBAAmB,EACnB,sBAAsB,EACtB,2BAA2B,EAC3B,iBAAiB,EACjB,sBAAsB,EACtB,mBAAmB,EACnB,iBAAiB,EACjB,WAAW,EACX,eAAe,GAChB,MAAM,0BAA0B,CAAA;AAmBjC,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,SAAS,EACT,oBAAoB,GACrB,MAAM,kBAAkB,CAAA"}
@@ -0,0 +1,50 @@
1
+ import type { Access, CollectionConfig } from 'payload';
2
+ /** The slug `@payloadcms/plugin-mcp` gives its key collection. */
3
+ export declare const MCP_API_KEYS_SLUG = "payload-mcp-api-keys";
4
+ /** What Payload puts on `req.user`. */
5
+ export type AccessUser = Parameters<Access>[0]['req']['user'];
6
+ /**
7
+ * True when `req.user` is an MCP key document rather than a person.
8
+ *
9
+ * Only a request authenticated by Payload's REST API-key strategy against the
10
+ * key collection carries this principal — which Payload 3.89.0 and later no
11
+ * longer register. A tool call through `/api/mcp` arrives as the key's user.
12
+ */
13
+ export declare function isMcpApiKeyPrincipal(user: AccessUser | undefined): boolean;
14
+ /**
15
+ * A principal a site should treat as a signed-in person.
16
+ *
17
+ * Use this wherever an access rule would otherwise say `Boolean(req.user)` —
18
+ * which is the rule Payload's own `defaultAccess` uses. A bearer credential
19
+ * presented over REST is a `req.user` and is not somebody signed in.
20
+ */
21
+ export declare function isSignedIn(user: AccessUser | undefined): user is NonNullable<AccessUser>;
22
+ /** `isSignedIn` as a Payload `Access` function. */
23
+ export declare const signedIn: Access;
24
+ /**
25
+ * An `overrideApiKeyCollection` for `mcpPlugin` that makes the key collection
26
+ * admin-only: read, create, update, delete and unlock all answer to `isAdmin`.
27
+ *
28
+ * `isAdmin` is the site's own rule, since only the site knows what an
29
+ * administrator is. Whatever it says, an MCP key principal is refused first, so
30
+ * a key can never manage keys.
31
+ *
32
+ * It touches `access` and nothing else, so it composes with other overrides:
33
+ *
34
+ * ```ts
35
+ * const keysAdminOnly = mcpApiKeyAccess(isAdmin)
36
+ *
37
+ * mcpPlugin({
38
+ * mcp: { tools: mcpTools.tools },
39
+ * overrideApiKeyCollection: collection => {
40
+ * const hardened = keysAdminOnly(collection)
41
+ * return { ...hardened, admin: { ...hardened.admin, group: 'Throughline' } }
42
+ * },
43
+ * })
44
+ * ```
45
+ *
46
+ * The plugin's field-level rules on the key's `user` relationship are left as
47
+ * shipped: a new key is bound to whoever creates it.
48
+ */
49
+ export declare function mcpApiKeyAccess(isAdmin: Access): (collection: CollectionConfig) => CollectionConfig;
50
+ //# sourceMappingURL=api-key-access.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-key-access.d.ts","sourceRoot":"","sources":["../../src/mcp/api-key-access.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAiCvD,kEAAkE;AAClE,eAAO,MAAM,iBAAiB,yBAAyB,CAAA;AAEvD,uCAAuC;AACvC,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAA;AAE7D;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,GAAG,OAAO,CAE1E;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,GAAG,IAAI,IAAI,WAAW,CAAC,UAAU,CAAC,CAExF;AAED,mDAAmD;AACnD,eAAO,MAAM,QAAQ,EAAE,MAA0C,CAAA;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,GACd,CAAC,UAAU,EAAE,gBAAgB,KAAK,gBAAgB,CAcpD"}
@@ -0,0 +1,94 @@
1
+ /*
2
+ Who may manage MCP keys, and who counts as signed in. Issue #157.
3
+
4
+ **The key collection.** `@payloadcms/plugin-mcp` brings `payload-mcp-api-keys`.
5
+ Older releases declared it with no `access` block, so every operation fell
6
+ through to Payload's `defaultAccess` — anybody signed in. Current ones scope each
7
+ person to their own keys. Neither is the right rule for a collection of
8
+ credentials: a key runs every tool as the person it is bound to, so who may mint,
9
+ read, rotate and revoke one is an administrator's decision. `mcpApiKeyAccess`
10
+ makes it one, through the plugin's `overrideApiKeyCollection`.
11
+
12
+ **The principal.** The collection sets `auth.useAPIKey: true`. Before Payload
13
+ 3.89.0 that registered Payload's API-key strategy globally, so
14
+ `Authorization: payload-mcp-api-keys API-Key <key>` was accepted on every REST
15
+ route and put the *key document* on `req.user`, tagged
16
+ `collection: 'payload-mcp-api-keys'`. It has no roles, so role checks denied it;
17
+ `Boolean(req.user)` did not. On a site whose draft rule was "published, or
18
+ anybody signed in", that served every draft to anyone holding a key, with no MCP
19
+ involved.
20
+
21
+ 3.89.0 excludes that slug from the strategy loop, and the suite's peer floor is
22
+ `^3.89.0` because of it. But one conditional in a dependency is not a control
23
+ anybody here would notice losing, so `isSignedIn` refuses the key principal
24
+ itself. Two things then have to fail for the hole to reopen.
25
+
26
+ **This cannot affect `/api/mcp`.** That endpoint resolves a presented key on its
27
+ own and runs the tool as the *user* the key points at, on the users collection,
28
+ with that person's roles. Only the REST strategy's principal carries the key
29
+ collection's slug, and that is the only principal refused here.
30
+ */
31
+ /** The slug `@payloadcms/plugin-mcp` gives its key collection. */
32
+ export const MCP_API_KEYS_SLUG = 'payload-mcp-api-keys';
33
+ /**
34
+ * True when `req.user` is an MCP key document rather than a person.
35
+ *
36
+ * Only a request authenticated by Payload's REST API-key strategy against the
37
+ * key collection carries this principal — which Payload 3.89.0 and later no
38
+ * longer register. A tool call through `/api/mcp` arrives as the key's user.
39
+ */
40
+ export function isMcpApiKeyPrincipal(user) {
41
+ return user?.collection === MCP_API_KEYS_SLUG;
42
+ }
43
+ /**
44
+ * A principal a site should treat as a signed-in person.
45
+ *
46
+ * Use this wherever an access rule would otherwise say `Boolean(req.user)` —
47
+ * which is the rule Payload's own `defaultAccess` uses. A bearer credential
48
+ * presented over REST is a `req.user` and is not somebody signed in.
49
+ */
50
+ export function isSignedIn(user) {
51
+ return Boolean(user) && !isMcpApiKeyPrincipal(user);
52
+ }
53
+ /** `isSignedIn` as a Payload `Access` function. */
54
+ export const signedIn = ({ req }) => isSignedIn(req.user);
55
+ /**
56
+ * An `overrideApiKeyCollection` for `mcpPlugin` that makes the key collection
57
+ * admin-only: read, create, update, delete and unlock all answer to `isAdmin`.
58
+ *
59
+ * `isAdmin` is the site's own rule, since only the site knows what an
60
+ * administrator is. Whatever it says, an MCP key principal is refused first, so
61
+ * a key can never manage keys.
62
+ *
63
+ * It touches `access` and nothing else, so it composes with other overrides:
64
+ *
65
+ * ```ts
66
+ * const keysAdminOnly = mcpApiKeyAccess(isAdmin)
67
+ *
68
+ * mcpPlugin({
69
+ * mcp: { tools: mcpTools.tools },
70
+ * overrideApiKeyCollection: collection => {
71
+ * const hardened = keysAdminOnly(collection)
72
+ * return { ...hardened, admin: { ...hardened.admin, group: 'Throughline' } }
73
+ * },
74
+ * })
75
+ * ```
76
+ *
77
+ * The plugin's field-level rules on the key's `user` relationship are left as
78
+ * shipped: a new key is bound to whoever creates it.
79
+ */
80
+ export function mcpApiKeyAccess(isAdmin) {
81
+ const adminOnly = args => (isMcpApiKeyPrincipal(args.req.user) ? false : isAdmin(args));
82
+ return collection => ({
83
+ ...collection,
84
+ access: {
85
+ ...collection.access,
86
+ read: adminOnly,
87
+ create: adminOnly,
88
+ update: adminOnly,
89
+ delete: adminOnly,
90
+ unlock: adminOnly,
91
+ },
92
+ });
93
+ }
94
+ //# sourceMappingURL=api-key-access.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-key-access.js","sourceRoot":"","sources":["../../src/mcp/api-key-access.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6BE;AAEF,kEAAkE;AAClE,MAAM,CAAC,MAAM,iBAAiB,GAAG,sBAAsB,CAAA;AAKvD;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAA4B;IAC/D,OAAQ,IAAoD,EAAE,UAAU,KAAK,iBAAiB,CAAA;AAChG,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,IAA4B;IACrD,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAA;AACrD,CAAC;AAED,mDAAmD;AACnD,MAAM,CAAC,MAAM,QAAQ,GAAW,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAe;IAEf,MAAM,SAAS,GAAW,IAAI,CAAC,EAAE,CAAC,CAAC,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;IAE/F,OAAO,UAAU,CAAC,EAAE,CAAC,CAAC;QACpB,GAAG,UAAU;QACb,MAAM,EAAE;YACN,GAAG,UAAU,CAAC,MAAM;YACpB,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,SAAS;SAClB;KACF,CAAC,CAAA;AACJ,CAAC"}
@@ -9,6 +9,12 @@
9
9
  * The role predicates stay with their servers. What counts as an audit reader is
10
10
  * not what counts as a forms author, and collapsing them would put one package's
11
11
  * policy in another's file.
12
+ *
13
+ * **`error` is reserved for a refusal.** `toPayloadMcpTool` reads it to set
14
+ * MCP's `isError` flag, so a *success* result must report failure some other
15
+ * way — the healthy shapes in this suite use `ok`, `healthy`, `message` and
16
+ * `details`. A success payload carrying an `error` string would reach the
17
+ * caller flagged as an error.
12
18
  */
13
19
  export declare function deniedEnvelope(reason: string): {
14
20
  error: string;
@@ -1 +1 @@
1
- {"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../src/mcp/envelope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAEhE"}
1
+ {"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../src/mcp/envelope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAEhE"}
@@ -9,6 +9,12 @@
9
9
  * The role predicates stay with their servers. What counts as an audit reader is
10
10
  * not what counts as a forms author, and collapsing them would put one package's
11
11
  * policy in another's file.
12
+ *
13
+ * **`error` is reserved for a refusal.** `toPayloadMcpTool` reads it to set
14
+ * MCP's `isError` flag, so a *success* result must report failure some other
15
+ * way — the healthy shapes in this suite use `ok`, `healthy`, `message` and
16
+ * `details`. A success payload carrying an `error` string would reach the
17
+ * caller flagged as an error.
12
18
  */
13
19
  export function deniedEnvelope(reason) {
14
20
  return { error: reason };
@@ -1 +1 @@
1
- {"version":3,"file":"envelope.js","sourceRoot":"","sources":["../../src/mcp/envelope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;AAC1B,CAAC"}
1
+ {"version":3,"file":"envelope.js","sourceRoot":"","sources":["../../src/mcp/envelope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;AAC1B,CAAC"}
@@ -8,4 +8,5 @@ export { createMcpToolCollector } from './collector.js';
8
8
  export type { AddToolsOptions, CreateMcpToolCollectorOptions, DeclareToolsOptions, McpToolCollector, McpToolDescriptor, } from './collector.js';
9
9
  export { deniedEnvelope } from './envelope.js';
10
10
  export { auditServerFor, mcpServerRefusal } from './audit-server.js';
11
+ export { MCP_API_KEYS_SLUG, isMcpApiKeyPrincipal, isSignedIn, mcpApiKeyAccess, signedIn, } from './api-key-access.js';
11
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACnD,YAAY,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAExC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAE5D,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACtE,YAAY,EACV,iBAAiB,EACjB,cAAc,EACd,uBAAuB,GACxB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AACvD,YAAY,EACV,eAAe,EACf,6BAA6B,EAC7B,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAE9C,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACnD,YAAY,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAExC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAE5D,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACtE,YAAY,EACV,iBAAiB,EACjB,cAAc,EACd,uBAAuB,GACxB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AACvD,YAAY,EACV,eAAe,EACf,6BAA6B,EAC7B,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAE9C,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAEpE,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,UAAU,EACV,eAAe,EACf,QAAQ,GACT,MAAM,qBAAqB,CAAA"}
package/dist/mcp/index.js CHANGED
@@ -4,4 +4,5 @@ export { toPayloadMcpTool, toPayloadMcpTools } from './payload-mcp.js';
4
4
  export { createMcpToolCollector } from './collector.js';
5
5
  export { deniedEnvelope } from './envelope.js';
6
6
  export { auditServerFor, mcpServerRefusal } from './audit-server.js';
7
+ export { MCP_API_KEYS_SLUG, isMcpApiKeyPrincipal, isSignedIn, mcpApiKeyAccess, signedIn, } from './api-key-access.js';
7
8
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAGnD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAGjD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAOtE,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AASvD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAE9C,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAGnD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAGjD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAOtE,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AASvD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAE9C,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAEpE,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,UAAU,EACV,eAAe,EACf,QAAQ,GACT,MAAM,qBAAqB,CAAA"}
@@ -12,6 +12,7 @@ export interface PayloadMcpTool {
12
12
  type: 'text';
13
13
  text: string;
14
14
  }>;
15
+ isError?: boolean;
15
16
  }>;
16
17
  }
17
18
  /**
@@ -61,21 +62,6 @@ export interface ToPayloadMcpToolOptions {
61
62
  */
62
63
  auditServer?: AuditMcpServer;
63
64
  }
64
- /**
65
- * Wraps one Throughline tool so Payload's MCP plugin can serve it.
66
- *
67
- * Three things are being translated, and none of them is the tool's logic:
68
- *
69
- * - **The schema.** `plugin-mcp` wants the raw shape; `withMeta` produces a
70
- * `z.object` around it. `.shape` is the whole conversion, which is why tools
71
- * must build their input with `withMeta` or `z.object` rather than an
72
- * arbitrary `ZodType`.
73
- * - **The context.** Payload hands the handler a request. Throughline's tools
74
- * take a `McpToolContext`, so one is built from it.
75
- * - **The result.** Payload wants MCP content blocks; Throughline's tools
76
- * return their own objects, exactly as they do through this package's own
77
- * handler, which does the same wrapping a layer up.
78
- */
79
65
  export declare function toPayloadMcpTool(tool: McpToolDefinition, options?: ToPayloadMcpToolOptions): PayloadMcpTool;
80
66
  /** Every tool in a server, in one call. */
81
67
  export declare function toPayloadMcpTools(tools: McpToolDefinition[], options?: ToPayloadMcpToolOptions): PayloadMcpTool[];
@@ -1 +1 @@
1
- {"version":3,"file":"payload-mcp.d.ts","sourceRoot":"","sources":["../../src/mcp/payload-mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,MAAM,EAAkB,iBAAiB,EAAE,MAAM,uCAAuC,CAAA;AACtG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AA0BrD,wEAAwE;AACxE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,CAAC,CAAC,WAAW,CAAA;IACzB,OAAO,EAAE,CACP,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,GAAG,EAAE,iBAAiB,EACtB,KAAK,EAAE,OAAO,KACX,OAAO,CAAC;QAAE,OAAO,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAA;CACjE;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAA;IAClG,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAC9B;AAED,MAAM,WAAW,uBAAuB;IACtC;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB;;;;OAIG;IACH,WAAW,CAAC,EAAE,cAAc,CAAA;CAC7B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,iBAAiB,EACvB,OAAO,GAAE,uBAA4B,GACpC,cAAc,CAkBhB;AA0ED,2CAA2C;AAC3C,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,iBAAiB,EAAE,EAC1B,OAAO,GAAE,uBAA4B,GACpC,cAAc,EAAE,CAElB"}
1
+ {"version":3,"file":"payload-mcp.d.ts","sourceRoot":"","sources":["../../src/mcp/payload-mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,MAAM,EAAkB,iBAAiB,EAAE,MAAM,uCAAuC,CAAA;AACtG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AA0BrD,wEAAwE;AACxE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,CAAC,CAAC,WAAW,CAAA;IACzB,OAAO,EAAE,CACP,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,GAAG,EAAE,iBAAiB,EACtB,KAAK,EAAE,OAAO,KACX,OAAO,CAAC;QAAE,OAAO,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;CACpF;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAA;IAClG,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAC9B;AAED,MAAM,WAAW,uBAAuB;IACtC;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB;;;;OAIG;IACH,WAAW,CAAC,EAAE,cAAc,CAAA;CAC7B;AAqDD,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,iBAAiB,EACvB,OAAO,GAAE,uBAA4B,GACpC,cAAc,CAqBhB;AA0ED,2CAA2C;AAC3C,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,iBAAiB,EAAE,EAC1B,OAAO,GAAE,uBAA4B,GACpC,cAAc,EAAE,CAElB"}
@@ -17,6 +17,42 @@ import { McpMetaSchema } from './meta.js';
17
17
  * return their own objects, exactly as they do through this package's own
18
18
  * handler, which does the same wrapping a layer up.
19
19
  */
20
+ /*
21
+ Whether a tool answered with a refusal, and so whether MCP's `isError` belongs
22
+ on the result.
23
+
24
+ Every server in the suite refuses the same way — `deniedEnvelope`, or a bare
25
+ `{ error }` built in the tool — and returns it as an ordinary value rather than
26
+ throwing, so a denial reads to the model as a denial rather than as a server
27
+ fault. What none of them did was set `isError`, so a refusal arrived as a
28
+ **successful** tool result carrying `{"error": …}`. An agent that checks the
29
+ protocol's flag instead of parsing the body read every refusal as a success —
30
+ including "Must be authenticated to use the publishing tools", which is the one
31
+ every `Bearer` call gets, because `plugin-mcp` assigns no `req.user`.
32
+
33
+ Found exercising `04` F-02 with a real MCP key, and recorded in the consuming
34
+ repository's #614. It is a reporting defect rather than an access one: nothing
35
+ was permitted that should not have been, and everything that was refused stayed
36
+ refused.
37
+
38
+ Here rather than in each tool for the reason the audit wrap below is here. This
39
+ is the one place every tool in the suite passes through, so a tool written
40
+ tomorrow is covered without its author knowing this file exists.
41
+
42
+ **The test is a non-empty string `error`, not a sole `error` key.** A refusal
43
+ that later grows a second field — a code, a retry hint — keeps its flag, which
44
+ is the likelier shape to drift. It costs the inverse risk: a *success* payload
45
+ carrying an `error` string would be flagged wrongly. Nothing in the suite does
46
+ that today — the healthy shapes report through `ok`, `healthy`, `message` and
47
+ `details` — and `payload-mcp.test.ts` pins it so a new one has to argue with a
48
+ test rather than slip past.
49
+ */
50
+ function isRefusal(result) {
51
+ if (typeof result !== 'object' || result === null)
52
+ return false;
53
+ const error = result['error'];
54
+ return typeof error === 'string' && error.trim() !== '';
55
+ }
20
56
  export function toPayloadMcpTool(tool, options = {}) {
21
57
  const shape = shapeOf(tool);
22
58
  return {
@@ -27,7 +63,10 @@ export function toPayloadMcpTool(tool, options = {}) {
27
63
  const context = contextFrom(req, options);
28
64
  try {
29
65
  const result = await tool.handler(args, context);
30
- return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
66
+ return {
67
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
68
+ ...(isRefusal(result) ? { isError: true } : {}),
69
+ };
31
70
  }
32
71
  catch (error) {
33
72
  await recordToolFailure(tool.name, error, args, context, options);
@@ -1 +1 @@
1
- {"version":3,"file":"payload-mcp.js","sourceRoot":"","sources":["../../src/mcp/payload-mcp.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"payload-mcp.js","sourceRoot":"","sources":["../../src/mcp/payload-mcp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { Access, CollectionConfig } from 'payload';
2
+ export declare const DEFAULT_JOB_FAILURES_SLUG = "job-failures";
3
+ /** What kind of thing failed. Adding a value is a schema change and needs a migration. */
4
+ export declare const JOB_FAILURE_KINDS: readonly ["job", "healthcheck"];
5
+ export type JobFailureKind = (typeof JOB_FAILURE_KINDS)[number];
6
+ export interface JobFailuresCollectionOptions {
7
+ /** Override the collection slug. Default: `'job-failures'`. */
8
+ slug?: string;
9
+ /** Read access. Default: users with the `admin` role. */
10
+ readAccess?: Access;
11
+ }
12
+ export declare function createJobFailuresCollection(options?: JobFailuresCollectionOptions): CollectionConfig;
13
+ //# sourceMappingURL=collection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../../src/observability/collection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAgBvD,eAAO,MAAM,yBAAyB,iBAAiB,CAAA;AAEvD,0FAA0F;AAC1F,eAAO,MAAM,iBAAiB,iCAAkC,CAAA;AAChE,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAA;AAE/D,MAAM,WAAW,4BAA4B;IAC3C,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAOD,wBAAgB,2BAA2B,CACzC,OAAO,GAAE,4BAAiC,GACzC,gBAAgB,CAuDlB"}
@@ -0,0 +1,76 @@
1
+ /*
2
+ Where a background job's failure is recorded.
3
+
4
+ Deliberately not the audit log. `audit-events` records who did what through
5
+ an MCP tool — `mcpServer` and `mcpTool` are `NOT NULL`, and every row answers
6
+ "which tool did this". A cron that ran out of retries is not an action anybody
7
+ took; it is an operational fact, with its own fields (a run id, a triggering
8
+ event) and its own lifetime (worth keeping for weeks, not forever). Giving it
9
+ a fake MCP server name would make that column mean nothing.
10
+
11
+ Read-only from outside the writer, like the audit log: the writer uses the
12
+ Local API, which bypasses these access rules.
13
+ */
14
+ export const DEFAULT_JOB_FAILURES_SLUG = 'job-failures';
15
+ /** What kind of thing failed. Adding a value is a schema change and needs a migration. */
16
+ export const JOB_FAILURE_KINDS = ['job', 'healthcheck'];
17
+ const adminOnly = ({ req }) => {
18
+ const roles = req.user?.['roles'] ?? [];
19
+ return roles.includes('admin');
20
+ };
21
+ export function createJobFailuresCollection(options = {}) {
22
+ return {
23
+ slug: options.slug ?? DEFAULT_JOB_FAILURES_SLUG,
24
+ admin: {
25
+ useAsTitle: 'summary',
26
+ defaultColumns: ['createdAt', 'kind', 'source', 'message'],
27
+ description: 'Background jobs that ran out of retries, and healthchecks that failed. Read-only; written by the failure handlers.',
28
+ },
29
+ access: {
30
+ read: options.readAccess ?? adminOnly,
31
+ create: () => false,
32
+ update: () => false,
33
+ delete: () => false,
34
+ },
35
+ fields: [
36
+ {
37
+ name: 'createdAt',
38
+ type: 'date',
39
+ required: true,
40
+ defaultValue: () => new Date().toISOString(),
41
+ admin: { position: 'sidebar', readOnly: true },
42
+ },
43
+ {
44
+ name: 'kind',
45
+ type: 'select',
46
+ required: true,
47
+ options: JOB_FAILURE_KINDS.map((value) => ({ label: value, value })),
48
+ },
49
+ {
50
+ name: 'source',
51
+ type: 'text',
52
+ required: true,
53
+ admin: { description: 'The function or check that failed, e.g. an Inngest function id.' },
54
+ },
55
+ { name: 'summary', type: 'text', required: true },
56
+ { name: 'message', type: 'textarea', required: true },
57
+ { name: 'errorName', type: 'text' },
58
+ {
59
+ name: 'runId',
60
+ type: 'text',
61
+ admin: { description: "The run's id in the job runner, for finding it in its dashboard." },
62
+ },
63
+ { name: 'triggerEvent', type: 'text' },
64
+ {
65
+ name: 'details',
66
+ type: 'json',
67
+ admin: { description: 'For a healthcheck: every failed check and what it said.' },
68
+ },
69
+ { name: 'environment', type: 'text' },
70
+ { name: 'commit', type: 'text' },
71
+ ],
72
+ indexes: [{ fields: ['createdAt'] }, { fields: ['source', 'createdAt'] }],
73
+ timestamps: false,
74
+ };
75
+ }
76
+ //# sourceMappingURL=collection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection.js","sourceRoot":"","sources":["../../src/observability/collection.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;EAYE;AAEF,MAAM,CAAC,MAAM,yBAAyB,GAAG,cAAc,CAAA;AAEvD,0FAA0F;AAC1F,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE,aAAa,CAAU,CAAA;AAUhE,MAAM,SAAS,GAAW,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE;IACpC,MAAM,KAAK,GAAI,GAAG,CAAC,IAAI,EAAE,CAAC,OAAO,CAA0B,IAAI,EAAE,CAAA;IACjE,OAAO,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;AAChC,CAAC,CAAA;AAED,MAAM,UAAU,2BAA2B,CACzC,UAAwC,EAAE;IAE1C,OAAO;QACL,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,yBAAyB;QAC/C,KAAK,EAAE;YACL,UAAU,EAAE,SAAS;YACrB,cAAc,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,CAAC;YAC1D,WAAW,EACT,oHAAoH;SACvH;QACD,MAAM,EAAE;YACN,IAAI,EAAE,OAAO,CAAC,UAAU,IAAI,SAAS;YACrC,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK;YACnB,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK;YACnB,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK;SACpB;QACD,MAAM,EAAE;YACN;gBACE,IAAI,EAAE,WAAW;gBACjB,IAAI,EAAE,MAAM;gBACZ,QAAQ,EAAE,IAAI;gBACd,YAAY,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBAC5C,KAAK,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE;aAC/C;YACD;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,QAAQ;gBACd,QAAQ,EAAE,IAAI;gBACd,OAAO,EAAE,iBAAiB,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;aACrE;YACD;gBACE,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,MAAM;gBACZ,QAAQ,EAAE,IAAI;gBACd,KAAK,EAAE,EAAE,WAAW,EAAE,iEAAiE,EAAE;aAC1F;YACD,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;YACjD,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE;YACrD,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE;YACnC;gBACE,IAAI,EAAE,OAAO;gBACb,IAAI,EAAE,MAAM;gBACZ,KAAK,EAAE,EAAE,WAAW,EAAE,kEAAkE,EAAE;aAC3F;YACD,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE;YACtC;gBACE,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,MAAM;gBACZ,KAAK,EAAE,EAAE,WAAW,EAAE,yDAAyD,EAAE;aAClF;YACD,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE;YACrC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE;SACjC;QACD,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,QAAQ,EAAE,WAAW,CAAC,EAAE,CAAC;QACzE,UAAU,EAAE,KAAK;KAClB,CAAA;AACH,CAAC"}
@@ -0,0 +1,9 @@
1
+ export { DEFAULT_REPORTED_HEADERS, ERROR_WEBHOOK_URL_ENV, NEVER_REPORTED_HEADERS, buildHealthcheckFailureReport, buildJobFailureReport, buildRequestErrorReport, clientPrefix, createErrorReporter, currentDeployment, describeErrorReporting, reportError, summariseReport, } from './report.js';
2
+ export type { BuildJobFailureReportInput, BuildRequestErrorReportOptions, ErrorReport, ErrorReporter, ErrorReporterOptions, HealthcheckFailureReport, JobFailureReport, ReportDeployment, ReportableHeaders, RequestErrorReport, } from './report.js';
3
+ export { DEFAULT_JOB_FAILURES_SLUG, JOB_FAILURE_KINDS, createJobFailuresCollection, } from './collection.js';
4
+ export type { JobFailureKind, JobFailuresCollectionOptions } from './collection.js';
5
+ export { createJobFailureWriter } from './writer.js';
6
+ export type { JobFailureWriter, JobFailureWriterOptions } from './writer.js';
7
+ export { getJobFailureWriter, jobFailuresPlugin } from './plugin.js';
8
+ export type { JobFailuresPluginOptions } from './plugin.js';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/observability/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,qBAAqB,EACrB,sBAAsB,EACtB,6BAA6B,EAC7B,qBAAqB,EACrB,uBAAuB,EACvB,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EACtB,WAAW,EACX,eAAe,GAChB,MAAM,aAAa,CAAA;AACpB,YAAY,EACV,0BAA0B,EAC1B,8BAA8B,EAC9B,WAAW,EACX,aAAa,EACb,oBAAoB,EACpB,wBAAwB,EACxB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,aAAa,CAAA;AAEpB,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,2BAA2B,GAC5B,MAAM,iBAAiB,CAAA;AACxB,YAAY,EAAE,cAAc,EAAE,4BAA4B,EAAE,MAAM,iBAAiB,CAAA;AAEnF,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AACpD,YAAY,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAE5E,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AACpE,YAAY,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA"}
@@ -0,0 +1,5 @@
1
+ export { DEFAULT_REPORTED_HEADERS, ERROR_WEBHOOK_URL_ENV, NEVER_REPORTED_HEADERS, buildHealthcheckFailureReport, buildJobFailureReport, buildRequestErrorReport, clientPrefix, createErrorReporter, currentDeployment, describeErrorReporting, reportError, summariseReport, } from './report.js';
2
+ export { DEFAULT_JOB_FAILURES_SLUG, JOB_FAILURE_KINDS, createJobFailuresCollection, } from './collection.js';
3
+ export { createJobFailureWriter } from './writer.js';
4
+ export { getJobFailureWriter, jobFailuresPlugin } from './plugin.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/observability/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,qBAAqB,EACrB,sBAAsB,EACtB,6BAA6B,EAC7B,qBAAqB,EACrB,uBAAuB,EACvB,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EACtB,WAAW,EACX,eAAe,GAChB,MAAM,aAAa,CAAA;AAcpB,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,2BAA2B,GAC5B,MAAM,iBAAiB,CAAA;AAGxB,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAGpD,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA"}
@@ -0,0 +1,21 @@
1
+ import { type BaseCorePluginOptions, type CorePlugin } from '@forumone/throughline-plugin-contract';
2
+ import { type JobFailuresCollectionOptions } from './collection.js';
3
+ import { type JobFailureWriter } from './writer.js';
4
+ export interface JobFailuresPluginOptions extends BaseCorePluginOptions, JobFailuresCollectionOptions {
5
+ }
6
+ /**
7
+ * Adds the `job-failures` collection and attaches its writer to the Payload
8
+ * instance, where `getJobFailureWriter` — and the workflows package's failure
9
+ * handlers — find it.
10
+ */
11
+ export declare const jobFailuresPlugin: CorePlugin<JobFailuresPluginOptions>;
12
+ /**
13
+ * The writer `jobFailuresPlugin` attached, or `undefined` when the plugin is
14
+ * not registered.
15
+ *
16
+ * Unlike `getAuditWriter` this does not throw: it is called from failure
17
+ * handlers, which fall back to logging and reporting when there is nowhere
18
+ * to write.
19
+ */
20
+ export declare function getJobFailureWriter(payload: object): JobFailureWriter | undefined;
21
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/observability/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,UAAU,EAEhB,MAAM,uCAAuC,CAAA;AAC9C,OAAO,EAA+B,KAAK,4BAA4B,EAAE,MAAM,iBAAiB,CAAA;AAChG,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAO3E,MAAM,WAAW,wBACf,SAAQ,qBAAqB,EAAE,4BAA4B;CAAG;AAEhE;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,UAAU,CAAC,wBAAwB,CA4BhE,CAAA;AAEH;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAEjF"}
@@ -0,0 +1,51 @@
1
+ import { getPluginRegistry, } from '@forumone/throughline-plugin-contract';
2
+ import { createJobFailuresCollection } from './collection.js';
3
+ import { createJobFailureWriter } from './writer.js';
4
+ import { defaultLogger } from '../logger/index.js';
5
+ const PLUGIN_ID = '@forumone/throughline-core/observability';
6
+ const PLUGIN_VERSION = '0.1.0';
7
+ const WRITER_SYMBOL = Symbol.for('@forumone/throughline/job-failure-writer');
8
+ /**
9
+ * Adds the `job-failures` collection and attaches its writer to the Payload
10
+ * instance, where `getJobFailureWriter` — and the workflows package's failure
11
+ * handlers — find it.
12
+ */
13
+ export const jobFailuresPlugin = (options) => (incomingConfig) => {
14
+ if (options.enabled === false)
15
+ return incomingConfig;
16
+ return {
17
+ ...incomingConfig,
18
+ collections: [...(incomingConfig.collections ?? []), createJobFailuresCollection(options)],
19
+ onInit: async (payload) => {
20
+ if (incomingConfig.onInit)
21
+ await incomingConfig.onInit(payload);
22
+ Object.defineProperty(payload, WRITER_SYMBOL, {
23
+ value: createJobFailureWriter({
24
+ payload,
25
+ collectionSlug: options.slug,
26
+ logger: options.logger ?? defaultLogger,
27
+ }),
28
+ enumerable: false,
29
+ writable: false,
30
+ configurable: false,
31
+ });
32
+ getPluginRegistry(payload).register({
33
+ id: PLUGIN_ID,
34
+ version: PLUGIN_VERSION,
35
+ capabilities: ['job-failures'],
36
+ });
37
+ },
38
+ };
39
+ };
40
+ /**
41
+ * The writer `jobFailuresPlugin` attached, or `undefined` when the plugin is
42
+ * not registered.
43
+ *
44
+ * Unlike `getAuditWriter` this does not throw: it is called from failure
45
+ * handlers, which fall back to logging and reporting when there is nowhere
46
+ * to write.
47
+ */
48
+ export function getJobFailureWriter(payload) {
49
+ return payload[WRITER_SYMBOL];
50
+ }
51
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/observability/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,iBAAiB,GAClB,MAAM,uCAAuC,CAAA;AAC9C,OAAO,EAAE,2BAA2B,EAAqC,MAAM,iBAAiB,CAAA;AAChG,OAAO,EAAE,sBAAsB,EAAyB,MAAM,aAAa,CAAA;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAElD,MAAM,SAAS,GAAG,0CAA0C,CAAA;AAC5D,MAAM,cAAc,GAAG,OAAO,CAAA;AAC9B,MAAM,aAAa,GAAG,MAAM,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAA;AAK5E;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAC5B,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,cAAc,EAAE,EAAE;IAC9B,IAAI,OAAO,CAAC,OAAO,KAAK,KAAK;QAAE,OAAO,cAAc,CAAA;IAEpD,OAAO;QACL,GAAG,cAAc;QACjB,WAAW,EAAE,CAAC,GAAG,CAAC,cAAc,CAAC,WAAW,IAAI,EAAE,CAAC,EAAE,2BAA2B,CAAC,OAAO,CAAC,CAAC;QAC1F,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YACxB,IAAI,cAAc,CAAC,MAAM;gBAAE,MAAM,cAAc,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;YAE/D,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,aAAa,EAAE;gBAC5C,KAAK,EAAE,sBAAsB,CAAC;oBAC5B,OAAO;oBACP,cAAc,EAAE,OAAO,CAAC,IAAI;oBAC5B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,aAAa;iBACxC,CAAC;gBACF,UAAU,EAAE,KAAK;gBACjB,QAAQ,EAAE,KAAK;gBACf,YAAY,EAAE,KAAK;aACpB,CAAC,CAAA;YAEF,iBAAiB,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC;gBAClC,EAAE,EAAE,SAAS;gBACb,OAAO,EAAE,cAAc;gBACvB,YAAY,EAAE,CAAC,cAAc,CAAC;aAC/B,CAAC,CAAA;QACJ,CAAC;KACF,CAAA;AACH,CAAC,CAAA;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAe;IACjD,OAAQ,OAAmC,CAAC,aAAa,CAAiC,CAAA;AAC5F,CAAC"}