@forumone/throughline-core 0.8.2 → 0.9.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,65 @@
1
1
  # @forumone/throughline-core
2
2
 
3
+ ## 0.9.1
4
+
5
+ ### Patch Changes
6
+
7
+ - d02772f: A refused MCP tool call now carries MCP's `isError` flag.
8
+
9
+ Every server in the suite answers a refusal with `{ error: … }` and returns it
10
+ rather than throwing, so that a denial reads to the model as a denial rather
11
+ than as a server fault. None of them set `isError`, so the refusal arrived as a
12
+ **successful** tool result that happened to contain an `error` key: a client
13
+ checking the protocol's flag instead of parsing the body read every refusal as
14
+ a success.
15
+
16
+ The publishing tools are where this was found, and they are the worst case for
17
+ it. `@payloadcms/plugin-mcp` assigns no `req.user` — it mutates `docs[0].user`
18
+ and passes it separately to its own CRUD tools — so `contextFrom(req)` reads
19
+ `null` and _every_ `Bearer`-authenticated publishing call is refused at
20
+ `actor.ts`'s identity guard. All of those refusals reported success.
21
+
22
+ Fixed in `toPayloadMcpTool`, which is the one adapter every tool in all six
23
+ servers passes through, so a tool written tomorrow is covered without its
24
+ author knowing the file exists.
25
+
26
+ **Nothing changes for a client that parses the body.** The content block is
27
+ byte-identical, and `isError` is absent rather than `false` on a success, so a
28
+ consumer reading `{ error }` today sees no difference. A consumer reading the
29
+ flag now sees the truth instead of its opposite.
30
+
31
+ The test is a non-empty string `error`, so a refusal that later grows a second
32
+ field — a code, a retry hint — keeps its flag. The corollary is now documented
33
+ on `deniedEnvelope`: `error` is reserved for refusals, and a success result
34
+ reports failure through `ok`, `healthy`, `message` or `details`.
35
+
36
+ Found exercising audit `04` F-02 against a real MCP key; forumone-2026#614.
37
+
38
+ ## 0.9.0
39
+
40
+ ### Minor Changes
41
+
42
+ - debfcd2: `system.error` now has a writer. Every MCP tool handler the suite serves is
43
+ wrapped, so a tool that throws records one `system.error` audit row — the
44
+ server, the tool, the caller, the caller's `_meta`, and the error's message —
45
+ before the throw propagates to the MCP client as it did before.
46
+
47
+ The row carries no stack and no arguments: `error_message` is readable by every
48
+ admin and editor, a stack names file paths, and a tool's input can hold a draft
49
+ body or a form submission. A failure inside the recording is logged and
50
+ swallowed, so this wrapper can never replace a tool's real error with its own.
51
+
52
+ `mcpServer` is resolved through a map rather than from the collector's own
53
+ server name, because the two vocabularies disagree: the components server
54
+ declares itself `components` and the audit enum's value is `component`. A
55
+ server that passes an audit writer and has no name in that map is now a
56
+ boot-time refusal instead of a row Payload silently rejects. New export:
57
+ `auditServerFor`.
58
+
59
+ Each of the six servers passes its audit writer to `collector.add`, alongside
60
+ the logger it already passed. A host wiring a tool by hand passes none and gets
61
+ the previous behaviour.
62
+
3
63
  ## 0.8.2
4
64
 
5
65
  ### Patch Changes
@@ -0,0 +1,13 @@
1
+ import { type AuditMcpServer } from '../audit/types.js';
2
+ /**
3
+ * The audit log's name for a collector server name, or `undefined` if there
4
+ * isn't one.
5
+ *
6
+ * `undefined` rather than a fallback: a row attributed to the wrong server is
7
+ * worse than a boot that stops and says which two lists to reconcile. The
8
+ * caller is `collector.add`, which throws — see `mcpServerRefusal`.
9
+ */
10
+ export declare function auditServerFor(serverName: string): AuditMcpServer | undefined;
11
+ /** The message `collector.add` throws with when a server has no audit name. */
12
+ export declare function mcpServerRefusal(serverName: string): string;
13
+ //# sourceMappingURL=audit-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-server.d.ts","sourceRoot":"","sources":["../../src/mcp/audit-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAwC1E;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAW7E;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAS3D"}
@@ -0,0 +1,68 @@
1
+ import { AUDIT_MCP_SERVERS } from '../audit/types.js';
2
+ /*
3
+ The collector's server names are not the audit log's server names, and one pair
4
+ disagrees.
5
+
6
+ `collector.ts` takes a `serverName` for its error messages — the string a
7
+ plugin passes to `declare` and `add`. The audit collection takes an
8
+ `mcpServer`, a Postgres enum (`enum_audit_events_mcp_server`). Six of the seven
9
+ names line up. The seventh does not:
10
+
11
+ collector audit enum
12
+ approvals approvals
13
+ audit audit
14
+ components -> component <- singular
15
+ forms forms
16
+ integrations integrations
17
+ publishing publishing
18
+ - payload
19
+
20
+ So `mcpServer: serverName as AuditMcpServer` compiles and is wrong for the
21
+ components server. It would be wrong *silently*: Payload rejects a select value
22
+ outside the options, `createAuditWriter` swallows every write failure by design
23
+ — "never let audit failures break the original action" — and the result is one
24
+ server whose rows are missing from the log with a `logger.error` nobody reads.
25
+
26
+ Hence a map, and hence `auditServerFor` refusing rather than guessing. The
27
+ refusal happens where the map is consulted, which is `add` — config time, once,
28
+ at boot — rather than inside a request.
29
+ */
30
+ const AUDIT_SERVER_BY_COLLECTOR_NAME = {
31
+ approvals: 'approvals',
32
+ audit: 'audit',
33
+ components: 'component',
34
+ forms: 'forms',
35
+ integrations: 'integrations',
36
+ payload: 'payload',
37
+ publishing: 'publishing',
38
+ };
39
+ /**
40
+ * The audit log's name for a collector server name, or `undefined` if there
41
+ * isn't one.
42
+ *
43
+ * `undefined` rather than a fallback: a row attributed to the wrong server is
44
+ * worse than a boot that stops and says which two lists to reconcile. The
45
+ * caller is `collector.add`, which throws — see `mcpServerRefusal`.
46
+ */
47
+ export function auditServerFor(serverName) {
48
+ /*
49
+ `hasOwn` rather than a bare index, because a bare index into an object
50
+ literal answers for `constructor` and `toString` — and the return type says
51
+ `AuditMcpServer | undefined`, so `auditServerFor('constructor')` would hand
52
+ back `Object` typed as a Postgres enum value and pass every check between
53
+ here and the insert.
54
+ */
55
+ return Object.hasOwn(AUDIT_SERVER_BY_COLLECTOR_NAME, serverName)
56
+ ? AUDIT_SERVER_BY_COLLECTOR_NAME[serverName]
57
+ : undefined;
58
+ }
59
+ /** The message `collector.add` throws with when a server has no audit name. */
60
+ export function mcpServerRefusal(serverName) {
61
+ return (`MCP server "${serverName}" passed an audit writer but has no name in the audit log's ` +
62
+ `mcpServer enum, so a \`system.error\` row for its tools could not be written. ` +
63
+ `Known collector names: ${Object.keys(AUDIT_SERVER_BY_COLLECTOR_NAME).join(', ')}. ` +
64
+ `Audit enum values: ${AUDIT_MCP_SERVERS.join(', ')}. Add "${serverName}" to the map in ` +
65
+ `core/src/mcp/audit-server.ts, and — if the enum needs a new value — to AUDIT_MCP_SERVERS ` +
66
+ `with a migration in the host that adds it to enum_audit_events_mcp_server.`);
67
+ }
68
+ //# sourceMappingURL=audit-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-server.js","sourceRoot":"","sources":["../../src/mcp/audit-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAuB,MAAM,mBAAmB,CAAA;AAE1E;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2BE;AACF,MAAM,8BAA8B,GAA6C;IAC/E,SAAS,EAAE,WAAW;IACtB,KAAK,EAAE,OAAO;IACd,UAAU,EAAE,WAAW;IACvB,KAAK,EAAE,OAAO;IACd,YAAY,EAAE,cAAc;IAC5B,OAAO,EAAE,SAAS;IAClB,UAAU,EAAE,YAAY;CACzB,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,UAAkB;IAC/C;;;;;;MAME;IACF,OAAO,MAAM,CAAC,MAAM,CAAC,8BAA8B,EAAE,UAAU,CAAC;QAC9D,CAAC,CAAC,8BAA8B,CAAC,UAAU,CAAC;QAC5C,CAAC,CAAC,SAAS,CAAA;AACf,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,gBAAgB,CAAC,UAAkB;IACjD,OAAO,CACL,eAAe,UAAU,8DAA8D;QACvF,gFAAgF;QAChF,0BAA0B,MAAM,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;QACpF,sBAAsB,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,UAAU,kBAAkB;QACxF,2FAA2F;QAC3F,4EAA4E,CAC7E,CAAA;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../../src/mcp/collector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAA;AAC9E,OAAO,EAAqB,KAAK,cAAc,EAAE,KAAK,uBAAuB,EAAE,MAAM,kBAAkB,CAAA;AA4CvG,4EAA4E;AAC5E,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,cAAc,EAAE,CAAA;IAChC;;;OAGG;IACH,OAAO,CAAC,WAAW,EAAE,SAAS,iBAAiB,EAAE,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAA;IACvF,sEAAsE;IACtE,GAAG,CAAC,KAAK,EAAE,iBAAiB,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI,CAAA;IAChE,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAA;IAC1B;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,eAAgB,SAAQ,uBAAuB,EAAE,mBAAmB;CAAG;AAExF,MAAM,MAAM,6BAA6B,GAAG,uBAAuB,CAAA;AAEnE;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,GAAE,6BAAkC,GAC1C,gBAAgB,CA+ElB"}
1
+ {"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../../src/mcp/collector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAA;AAE9E,OAAO,EAAqB,KAAK,cAAc,EAAE,KAAK,uBAAuB,EAAE,MAAM,kBAAkB,CAAA;AA4CvG,4EAA4E;AAC5E,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,cAAc,EAAE,CAAA;IAChC;;;OAGG;IACH,OAAO,CAAC,WAAW,EAAE,SAAS,iBAAiB,EAAE,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAA;IACvF,sEAAsE;IACtE,GAAG,CAAC,KAAK,EAAE,iBAAiB,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI,CAAA;IAChE,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAA;IAC1B;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,eAAgB,SAAQ,uBAAuB,EAAE,mBAAmB;CAAG;AAExF,MAAM,MAAM,6BAA6B,GAAG,uBAAuB,CAAA;AAEnE;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,GAAE,6BAAkC,GAC1C,gBAAgB,CAkGlB"}
@@ -1,3 +1,4 @@
1
+ import { auditServerFor, mcpServerRefusal } from './audit-server.js';
1
2
  import { toPayloadMcpTools } from './payload-mcp.js';
2
3
  /**
3
4
  * Somewhere for the plugins to put their tools, that the host can pass to
@@ -53,7 +54,25 @@ export function createMcpToolCollector(options = {}) {
53
54
  },
54
55
  add(incoming, addOptions = {}) {
55
56
  const { serverName = 'an unnamed server', ...toolOptions } = addOptions;
56
- for (const tool of toPayloadMcpTools(incoming, { ...options, ...toolOptions })) {
57
+ /*
58
+ Which `mcpServer` this server's `system.error` rows carry, resolved once
59
+ per server at `onInit` rather than per request — and refused rather than
60
+ guessed, because the collector's names and the audit enum's names are two
61
+ lists that already disagree about one entry. See `audit-server.ts`.
62
+
63
+ Only when a writer was actually passed. A host wiring tools by hand
64
+ supplies no `audit`, and it should not have to invent a server name to do
65
+ that.
66
+ */
67
+ const auditServer = auditServerFor(serverName);
68
+ if (toolOptions.audit && !auditServer) {
69
+ throw new Error(mcpServerRefusal(serverName));
70
+ }
71
+ for (const tool of toPayloadMcpTools(incoming, {
72
+ ...options,
73
+ ...toolOptions,
74
+ ...(auditServer ? { auditServer } : {}),
75
+ })) {
57
76
  const slot = slots.get(tool.name);
58
77
  if (!slot) {
59
78
  throw new Error(`${serverName} built an MCP tool called "${tool.name}" that it never declared. ` +
@@ -1 +1 @@
1
- {"version":3,"file":"collector.js","sourceRoot":"","sources":["../../src/mcp/collector.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"collector.js","sourceRoot":"","sources":["../../src/mcp/collector.ts"],"names":[],"mappings":"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"}
@@ -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"}
@@ -7,4 +7,5 @@ export type { PayloadMcpRequest, PayloadMcpTool, ToPayloadMcpToolOptions, } from
7
7
  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
+ export { auditServerFor, mcpServerRefusal } from './audit-server.js';
10
11
  //# 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"}
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"}
package/dist/mcp/index.js CHANGED
@@ -3,4 +3,5 @@ export { auditContext } from './audit-context.js';
3
3
  export { toPayloadMcpTool, toPayloadMcpTools } from './payload-mcp.js';
4
4
  export { createMcpToolCollector } from './collector.js';
5
5
  export { deniedEnvelope } from './envelope.js';
6
+ export { auditServerFor, mcpServerRefusal } from './audit-server.js';
6
7
  //# 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"}
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,5 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import type { Logger, McpToolDefinition } from '@forumone/throughline-plugin-contract';
3
+ import type { AuditMcpServer } from '../audit/types.js';
4
+ import type { AuditWriter } from '../audit/writer.js';
3
5
  /** The tool shape `plugin-mcp` accepts under its `mcp.tools` option. */
4
6
  export interface PayloadMcpTool {
5
7
  name: string;
@@ -10,6 +12,7 @@ export interface PayloadMcpTool {
10
12
  type: 'text';
11
13
  text: string;
12
14
  }>;
15
+ isError?: boolean;
13
16
  }>;
14
17
  }
15
18
  /**
@@ -44,22 +47,21 @@ export interface ToPayloadMcpToolOptions {
44
47
  */
45
48
  apiKeyName?: string;
46
49
  logger?: Logger;
50
+ /**
51
+ * Where a tool's unhandled throw is recorded, as `system.error`.
52
+ *
53
+ * Optional, and absent means the previous behaviour: the throw reaches
54
+ * `plugin-mcp`, becomes a JSON-RPC error, and leaves no trace. Every server
55
+ * in the suite passes one; a host wiring a tool by hand need not.
56
+ */
57
+ audit?: AuditWriter;
58
+ /**
59
+ * Which `mcpServer` those rows are attributed to. Derived from the collector's
60
+ * `serverName` by `auditServerFor`, which is not the identity function — see
61
+ * `audit-server.ts`.
62
+ */
63
+ auditServer?: AuditMcpServer;
47
64
  }
48
- /**
49
- * Wraps one Throughline tool so Payload's MCP plugin can serve it.
50
- *
51
- * Three things are being translated, and none of them is the tool's logic:
52
- *
53
- * - **The schema.** `plugin-mcp` wants the raw shape; `withMeta` produces a
54
- * `z.object` around it. `.shape` is the whole conversion, which is why tools
55
- * must build their input with `withMeta` or `z.object` rather than an
56
- * arbitrary `ZodType`.
57
- * - **The context.** Payload hands the handler a request. Throughline's tools
58
- * take a `McpToolContext`, so one is built from it.
59
- * - **The result.** Payload wants MCP content blocks; Throughline's tools
60
- * return their own objects, exactly as they do through this package's own
61
- * handler, which does the same wrapping a layer up.
62
- */
63
65
  export declare function toPayloadMcpTool(tool: McpToolDefinition, options?: ToPayloadMcpToolOptions): PayloadMcpTool;
64
66
  /** Every tool in a server, in one call. */
65
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;AAwBtG,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;CAChB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,iBAAiB,EACvB,OAAO,GAAE,uBAA4B,GACpC,cAAc,CAYhB;AAED,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"}
@@ -1,5 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { defaultLogger } from '../logger/index.js';
3
+ import { auditContext } from './audit-context.js';
4
+ import { McpMetaSchema } from './meta.js';
3
5
  /**
4
6
  * Wraps one Throughline tool so Payload's MCP plugin can serve it.
5
7
  *
@@ -15,6 +17,42 @@ import { defaultLogger } from '../logger/index.js';
15
17
  * return their own objects, exactly as they do through this package's own
16
18
  * handler, which does the same wrapping a layer up.
17
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
+ }
18
56
  export function toPayloadMcpTool(tool, options = {}) {
19
57
  const shape = shapeOf(tool);
20
58
  return {
@@ -22,11 +60,85 @@ export function toPayloadMcpTool(tool, options = {}) {
22
60
  description: tool.description,
23
61
  parameters: shape,
24
62
  handler: async (args, req) => {
25
- const result = await tool.handler(args, contextFrom(req, options));
26
- return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
63
+ const context = contextFrom(req, options);
64
+ try {
65
+ const result = await tool.handler(args, context);
66
+ return {
67
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
68
+ ...(isRefusal(result) ? { isError: true } : {}),
69
+ };
70
+ }
71
+ catch (error) {
72
+ await recordToolFailure(tool.name, error, args, context, options);
73
+ throw error;
74
+ }
27
75
  },
28
76
  };
29
77
  }
78
+ /*
79
+ The one place a tool's crash becomes a row, and the reason `system.error` had a
80
+ name in the taxonomy and no writer.
81
+
82
+ Audit 12 H3: `'system.error'` was declared in `audit/types.ts`, present in the
83
+ Postgres enum, and written by nothing — while `observability.md` opened with
84
+ "the audit log is the single most useful debugging tool" and listed errors among
85
+ what lands there. A tool that returns `deniedEnvelope` or an `{ error }` result
86
+ audits itself; a tool that *throws* did not, so the failures most worth reading
87
+ were the only ones absent.
88
+
89
+ This is the choke point every tool in the suite passes through — all six
90
+ servers, every tool — so wrapping here is one wrap rather than one per tool,
91
+ and a tool added tomorrow is covered without its author knowing this file
92
+ exists. It also covers the tools that audit nothing else: `list_components`,
93
+ `get_contract`, `get_variants`, `get_tokens` and the five audit reads write no
94
+ row on success, so a crash is the only thing they have ever recorded.
95
+
96
+ Three properties it has to have, in order:
97
+
98
+ **The original error still propagates.** `plugin-mcp` turns a throw into a
99
+ JSON-RPC error and the client needs that. Recording is a side effect, not a
100
+ handler.
101
+
102
+ **Recording cannot itself fail the call.** `createAuditWriter` already swallows
103
+ its own write failures, but `options.audit` is an interface and a caller may
104
+ pass something less careful, so the `await` is wrapped too. An audit miss is a
105
+ gap in a log; a throw from here would replace the tool's real error with this
106
+ file's, which is the one outcome that would make debugging worse rather than
107
+ better.
108
+
109
+ **The message is the error's, and nothing else.** No stack: `errorMessage` is a
110
+ `varchar` on a table admins and editors can read through the admin UI, and a
111
+ stack names file paths and sometimes argument values. The arguments are not
112
+ recorded either — a tool's input can carry a form submission or a draft — but
113
+ `_meta` is, because the agent's own prompt and reasoning are what make a crash
114
+ row legible, and they are already recorded on every successful write.
115
+ */
116
+ async function recordToolFailure(toolName, error, args, context, options) {
117
+ const { audit, auditServer } = options;
118
+ const logger = context.logger ?? defaultLogger;
119
+ const message = error instanceof Error ? error.message : String(error);
120
+ logger.error(`MCP tool "${toolName}" threw`, { tool: toolName, error: message });
121
+ if (!audit || !auditServer)
122
+ return;
123
+ try {
124
+ const meta = McpMetaSchema.safeParse(args['_meta']);
125
+ await audit({
126
+ ...auditContext(context, meta.success ? meta.data : undefined),
127
+ action: 'system.error',
128
+ mcpServer: auditServer,
129
+ mcpTool: toolName,
130
+ summary: `${toolName} threw: ${message}`,
131
+ success: false,
132
+ errorMessage: message,
133
+ });
134
+ }
135
+ catch (recordError) {
136
+ logger.error('Recording an MCP tool failure failed', {
137
+ tool: toolName,
138
+ error: String(recordError),
139
+ });
140
+ }
141
+ }
30
142
  /** Every tool in a server, in one call. */
31
143
  export function toPayloadMcpTools(tools, options = {}) {
32
144
  return tools.map(tool => toPayloadMcpTool(tool, options));
@@ -1 +1 @@
1
- {"version":3,"file":"payload-mcp.js","sourceRoot":"","sources":["../../src/mcp/payload-mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AA8DlD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,gBAAgB,CAC9B,IAAuB,EACvB,UAAmC,EAAE;IAErC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAE3B,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,UAAU,EAAE,KAAK;QACjB,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;YAC3B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAA;YAClE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAA;QAC/E,CAAC;KACF,CAAA;AACH,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,iBAAiB,CAC/B,KAA0B,EAC1B,UAAmC,EAAE;IAErC,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAA;AAC3D,CAAC;AAED,SAAS,OAAO,CAAC,IAAuB;IACtC,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAA;IAC/B,IAAI,MAAM,YAAY,CAAC,CAAC,SAAS;QAAE,OAAO,MAAM,CAAC,KAAsB,CAAA;IACvE,MAAM,IAAI,KAAK,CACb,SAAS,IAAI,CAAC,IAAI,2EAA2E;QAC3F,0FAA0F,CAC7F,CAAA;AACH,CAAC;AAED,SAAS,WAAW,CAAC,GAAsB,EAAE,OAAgC;IAC3E,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI;QACnB,CAAC,CAAC;YACE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC;YAC7B,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YACnC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YACnD,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,IAAI,CAAC,KAAkB,CAAC,CAAC,CAAC,EAAE;YACxE,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,IAAI,CAAC,MAAmB,CAAC,CAAC,CAAC,EAAE;SAC5E;QACH,CAAC,CAAC,IAAI,CAAA;IAER,OAAO;QACL,IAAI;QACJ;;;;;UAKE;QACF,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,CAAC,GAAG,CAAC,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;QACjF,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,GAAG,CAAC,OAAO,EAAE,MAAM,IAAI,aAAa;KAC/D,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"payload-mcp.js","sourceRoot":"","sources":["../../src/mcp/payload-mcp.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forumone/throughline-core",
3
- "version": "0.8.2",
3
+ "version": "0.9.1",
4
4
  "description": "Core plumbing for Throughline: audit log, MCP authentication and handler, event taxonomy + Inngest client factory, logger, shared utilities.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",