@ultimat3/action 22.9.1 → 22.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ultimat3/action",
3
- "version": "22.9.1",
3
+ "version": "22.10.0",
4
4
  "description": "The action primitive: one declaration projected to route, OpenAPI, client, MCP tool, job handle, tests",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -34,11 +34,11 @@
34
34
  "test": "bun test"
35
35
  },
36
36
  "dependencies": {
37
- "@ultimat3/cache": "22.9.1",
38
- "@ultimat3/core": "22.9.1",
39
- "@ultimat3/entity": "22.9.1",
40
- "@ultimat3/http": "22.9.1",
41
- "@ultimat3/policy": "22.9.1",
42
- "@ultimat3/schema": "22.9.1"
37
+ "@ultimat3/cache": "22.10.0",
38
+ "@ultimat3/core": "22.10.0",
39
+ "@ultimat3/entity": "22.10.0",
40
+ "@ultimat3/http": "22.10.0",
41
+ "@ultimat3/policy": "22.10.0",
42
+ "@ultimat3/schema": "22.10.0"
43
43
  }
44
44
  }
package/src/action.ts CHANGED
@@ -63,6 +63,33 @@ export interface ActionMcp {
63
63
  * actor's admin permissions) hands `@ultimat3/mcp` a predicate instead.
64
64
  */
65
65
  readonly visibleTo?: readonly string[];
66
+ /**
67
+ * The tool's display name in an MCP client's UI (`title`, MCP 2025-06-18). Contract text, like
68
+ * `description`. Omitted: none is published and the client shows the tool name.
69
+ */
70
+ readonly title?: string;
71
+ /**
72
+ * MCP tool annotations, overriding what `@ultimat3/mcp` derives key by key. An action derives
73
+ * `readOnlyHint: false`, `destructiveHint: true` and `idempotentHint` from `idempotent`. Hints for
74
+ * a client's confirmation UI — the policy decides every call whatever they say.
75
+ *
76
+ * ```ts
77
+ * mcp: { expose: true, annotations: { destructiveHint: false, openWorldHint: true } },
78
+ * ```
79
+ */
80
+ readonly annotations?: McpAnnotationHints;
81
+ }
82
+
83
+ /**
84
+ * MCP's four tool hints, as the spec spells them. Declared here (tier 3) and in `@ultimat3/query`
85
+ * because `@ultimat3/mcp` (tier 4) is the reader and cannot be imported; its `McpToolAnnotations`
86
+ * is the same shape.
87
+ */
88
+ export interface McpAnnotationHints {
89
+ readonly readOnlyHint?: boolean;
90
+ readonly destructiveHint?: boolean;
91
+ readonly idempotentHint?: boolean;
92
+ readonly openWorldHint?: boolean;
66
93
  }
67
94
 
68
95
  export interface ActionRateLimit {
package/src/errors.ts CHANGED
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import {
8
8
  assertNever,
9
+ deniedCallerFix,
9
10
  ERROR_DOCS_URL,
10
11
  hasErrorCode,
11
12
  registerErrorCodes,
@@ -136,6 +137,7 @@ export class ActionDeniedError extends UltimateError {
136
137
  code,
137
138
  cause: `${action} denied: ${denialReason(denial)}`,
138
139
  fix: `x policy explain ${action} --json # shows which clause decided and why`,
140
+ callerFix: deniedCallerFix(),
139
141
  });
140
142
  this.denial = denial;
141
143
  }
@@ -203,11 +205,25 @@ export class InputInvalidError extends UltimateError {
203
205
  * `formatIssues` here would pull that package's whole barrel into every browser bundle holding
204
206
  * the typed client.
205
207
  */
206
- constructor(name: string, detail: string, issues?: readonly ValidationIssue[]) {
208
+ constructor(
209
+ name: string,
210
+ detail: string,
211
+ issues?: readonly ValidationIssue[],
212
+ /**
213
+ * What `name` names, in the cause: `'action'` (the default), or `'tool'` when `@ultimat3/mcp`
214
+ * refuses arguments against a tool's published schema — a query's or a meta tool's, not only an
215
+ * action's.
216
+ */
217
+ subject: 'action' | 'tool' = 'action',
218
+ ) {
207
219
  super({
208
220
  code: 'X_INPUT_INVALID',
209
- cause: `input for action "${name}" failed validation: ${detail}`,
221
+ cause: `input for ${subject} "${name}" failed validation: ${detail}`,
210
222
  fix: `x actions describe ${name} --json # prints the expected input schema`,
223
+ // A remote caller holds the published schema — the MCP `inputSchema`, the OpenAPI request
224
+ // body — and cannot run `x actions describe`.
225
+ callerFix:
226
+ 'correct the fields named in cause to match the published input schema (the tool inputSchema, or the OpenAPI request body) and send again — the same input is refused the same way',
211
227
  ...(issues === undefined ? {} : { meta: { issues } }),
212
228
  });
213
229
  this.issues = issues;
package/src/index.ts CHANGED
@@ -54,6 +54,7 @@ export type {
54
54
  ActionRowArgs,
55
55
  AnyAction,
56
56
  InvokeOptions,
57
+ McpAnnotationHints,
57
58
  McpDescriptorMeta,
58
59
  } from './action';
59
60
  export { action, describeAction, isAction } from './action';