@agentproto/tool 0.5.0 → 0.5.2

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/dist/index.d.ts CHANGED
@@ -159,6 +159,12 @@ interface PaginatedOptions<TItem> {
159
159
  * keeps a tool whose rows pile up (terminals) under the MCP output cap.
160
160
  */
161
161
  defaultLimit?: number;
162
+ /**
163
+ * Add `total` (the size of the list the handler returned, i.e. after any
164
+ * filtering) to the NON-paginated wrapper too. Default off: the legacy
165
+ * `{ [itemKey]: [...] }` shape stays byte-identical for existing tools.
166
+ */
167
+ includeTotal?: boolean;
162
168
  }
163
169
  /**
164
170
  * Pagination transformer — adds {@link pageParamsShape} to the input shape
@@ -166,14 +172,14 @@ interface PaginatedOptions<TItem> {
166
172
  * array) with the shared cursor/limit semantics:
167
173
  *
168
174
  * - `limit`/`cursor` present → the `{ items, nextCursor?, total }` page
169
- * envelope via `paginate` + `toolText` (cursor/limit semantics are the
175
+ * envelope via `paginate` (cursor/limit semantics are the
170
176
  * exact ones the daemon's list tools already depend on).
171
177
  * - neither present → the legacy `{ [itemKey]: [...] }` wrapper, no
172
- * envelope fields.
178
+ * envelope fields (unless `includeTotal` adds `total`).
173
179
  *
174
180
  * Rows are COMPACT by default (`project`); `full: true` / `compact: false`
175
181
  * returns the unprojected records. `fields` is a per-item allowlist
176
- * applied on the paginated envelope branch (matching existing behavior).
182
+ * applied on every branch (paginated, truncated and plain wrapper).
177
183
  * An explicit `fields` list is itself a projection, bounded by what the
178
184
  * caller names, so on that branch it is applied to the FULL record and the
179
185
  * compact projection is skipped (unless `compact: true` is explicit) —
package/dist/index.mjs CHANGED
@@ -132,7 +132,7 @@ function textResult(text) {
132
132
  return { content: [{ type: "text", text }] };
133
133
  }
134
134
  function paginated(opts) {
135
- const { project, keyOf, maxLimit = 200, itemKey = "items", defaultLimit } = opts;
135
+ const { project, keyOf, maxLimit = 200, itemKey = "items", defaultLimit, includeTotal } = opts;
136
136
  return {
137
137
  name: "paginated",
138
138
  wrapShape: (shape) => ({ ...shape, ...pageParamsShape }),
@@ -141,20 +141,30 @@ function paginated(opts) {
141
141
  const items = await handler(input);
142
142
  const full = params.full === true;
143
143
  const compact = full ? false : params.compact !== false;
144
+ const projectRows = compact && (params.fields === void 0 || params.compact === true);
145
+ const render = (rows) => {
146
+ const projected = projectRows ? rows.map(project) : [...rows];
147
+ const fields = params.fields;
148
+ return fields === void 0 ? projected : projected.map((row) => Object.fromEntries(Object.entries(row).filter(([key]) => fields.includes(key))));
149
+ };
144
150
  if (params.limit !== void 0 || params.cursor !== void 0) {
145
151
  const page = paginate(items, params, { maxLimit, keyOf });
146
- const projectRows = compact && (params.fields === void 0 || params.compact === true);
147
- return projectRows ? textResult(toolText({ ...page, items: page.items.map(project) }, params)) : textResult(toolText(page, params));
152
+ return textResult(JSON.stringify({ ...page, items: render(page.items) }));
148
153
  }
149
154
  if (defaultLimit !== void 0 && items.length > defaultLimit) {
150
155
  const page = paginate(items, { limit: defaultLimit }, { maxLimit: Math.max(maxLimit, defaultLimit), keyOf });
151
- const rows2 = compact ? page.items.map(project) : page.items;
152
156
  return textResult(
153
- JSON.stringify({ [itemKey]: rows2, total: page.total, truncated: true, nextCursor: page.nextCursor })
157
+ JSON.stringify({
158
+ [itemKey]: render(page.items),
159
+ total: page.total,
160
+ truncated: true,
161
+ nextCursor: page.nextCursor
162
+ })
154
163
  );
155
164
  }
156
- const rows = compact ? items.map(project) : items;
157
- return textResult(JSON.stringify({ [itemKey]: rows }));
165
+ return textResult(
166
+ JSON.stringify({ [itemKey]: render(items), ...includeTotal ? { total: items.length } : {} })
167
+ );
158
168
  }
159
169
  };
160
170
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/create-tool.ts","../src/spec.ts","../src/envelope.ts","../src/transformers.ts","../src/index.ts"],"names":["rows"],"mappings":";;;;;;;;;;;;;AA0BA,SAAS,gBAAgB,GAAA,EAAuD;AAC9E,EAAA,OAAO,MAAA,CAAO,WAAA;AAAA,IACZ,OAAO,OAAA,CAAQ,GAAG,EAAE,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA,CAAE,QAAQ,UAAA,EAAY,KAAK,EAAE,WAAA,EAAY,EAAG,CAAC,CAAC;AAAA,GACrF;AACF;AAwBA,eAAsB,UAAA,CAKpB,QACA,IAAA,EACsD;AACtD,EAAA,MAAM,MAAA,GAAS,WAAsC,MAAM,CAAA;AAC3D,EAAA,MAAM,OAAO,IAAA,CAAK,IAAA,CAAK,GAAA,EAAK,MAAA,CAAO,IAAI,SAAS,CAAA;AAChD,EAAA,MAAM,cAAc,kBAAA,CAAmB;AAAA,IACrC,MAAA,EAAQ,oBAAA;AAAA,IACR,GAAG,gBAAgB,MAA4C;AAAA,GAChE,CAAA;AACD,EAAA,MAAM,WAAW,MAAA,CAAO,SAAA;AAAA,IACtB,IAAA,CAAK,IAAA,IAAQ,CAAA,EAAA,EAAK,MAAA,CAAO,IAAI;;AAAA,EAAO,OAAO,WAAW;AAAA,CAAA;AAAA,IACtD;AAAA,GACF;AACA,EAAA,IAAI,CAAC,KAAK,MAAA,EAAQ;AAChB,IAAA,MAAM,MAAM,OAAA,CAAQ,IAAI,GAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AAC9C,IAAA,MAAM,SAAA,CAAU,IAAA,EAAM,QAAA,EAAU,MAAM,CAAA;AAAA,EACxC;AACA,EAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,QAAA,EAAS;AAClC;AChEO,IAAM,QAAA,GAGT;AAAA,EACF,IAAA,EAAM,MAAA;AAAA,EACN,GAAA,EAAK,EAAA;AAAA,EACL,aAAA,EAAe,oBAAA;AAAA,EACf,MAAA,EAAQ,CAAC,CAAA,KAAM,CAAA,EAAG,EAAE,EAAE,CAAA,QAAA,CAAA;AAAA,EACtB,MAAA,EAAQ,CAAC,MAAA,KACP,UAAA,CAAW,MAAM,CAAA;AAAA,EACnB,KAAA,EAAO,CAAC,MAAA,KAAW;AACjB,IAAA,MAAM,CAAA,GAAI,kBAAkB,MAAM,CAAA;AAClC,IAAA,MAAM,KAAK,CAAA,CAAE,WAAA;AAOb,IAAA,MAAM,WAAA,GAAuC;AAAA,MAC3C,IAAI,EAAA,CAAG,EAAA;AAAA,MACP,MAAM,EAAA,CAAG,IAAA;AAAA,MACT,aAAa,EAAA,CAAG,WAAA;AAAA,MAChB,SAAS,EAAA,CAAG,OAAA;AAAA,MACZ,GAAI,EAAA,CAAG,OAAA,KAAY,UAAa,EAAE,OAAA,EAAS,GAAG,OAAA,EAAQ;AAAA,MACtD,GAAI,EAAA,CAAG,QAAA,KAAa,UAAa,EAAE,QAAA,EAAU,GAAG,QAAA,EAAS;AAAA,MACzD,GAAI,EAAA,CAAG,QAAA,KAAa,UAAa,EAAE,QAAA,EAAU,GAAG,QAAA,EAAS;AAAA,MACzD,GAAI,EAAA,CAAG,UAAA,KAAe,UAAa,EAAE,SAAA,EAAW,GAAG,UAAA,EAAW;AAAA,MAC9D,GAAI,EAAA,CAAG,UAAA,KAAe,UAAa,EAAE,SAAA,EAAW,GAAG,UAAA,EAAW;AAAA,MAC9D,GAAI,EAAA,CAAG,UAAA,KAAe,UAAa,EAAE,SAAA,EAAW,GAAG,UAAA,EAAW;AAAA,MAC9D,GAAI,EAAA,CAAG,UAAA,KAAe,UAAa,EAAE,UAAA,EAAY,GAAG,UAAA,EAAW;AAAA,MAC/D,GAAI,EAAA,CAAG,IAAA,KAAS,UAAa,EAAE,IAAA,EAAM,GAAG,IAAA,EAAK;AAAA,MAC7C,GAAI,EAAA,CAAG,QAAA,KAAa,UAAa,EAAE,QAAA,EAAU,GAAG,QAAA,EAAS;AAAA;AAAA,MAEzD,GAAI,EAAA,CAAG,MAAA,KAAW,UAAa,EAAE,MAAA,EAAQ,GAAG,MAAA,EAAO;AAAA,MACnD,GAAI,EAAA,CAAG,OAAA,KAAY,UAAa,EAAE,OAAA,EAAS,GAAG,OAAA;AAAQ,KACxD;AACA,IAAA,OAAO,EAAE,WAAA,EAAa,IAAA,EAAM,CAAA,CAAE,IAAA,EAAK;AAAA,EACrC;AACF;AAEO,IAAM,SAAA,GAAY,YAAY,QAAQ;ACrBtC,SAAS,aAAa,OAAA,EAAgC;AAC3D,EAAA,OAAO,MAAA,CAAO,KAAK,IAAA,CAAK,SAAA,CAAU,OAAO,CAAA,EAAG,MAAM,CAAA,CAAE,QAAA,CAAS,WAAW,CAAA;AAC1E;AAEA,IAAM,mBAAA,GAAsB,EAAE,MAAA,CAAO;AAAA,EACnC,CAAA,EAAG,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,CAAE,MAAA,EAAO,EAAG,CAAA,CAAE,MAAA,EAAO,EAAG,CAAA,CAAE,IAAA,EAAM,CAAC,CAAA;AAAA,EAC7C,GAAG,CAAA,CAAE,MAAA,GAAS,GAAA,EAAI,CAAE,IAAI,CAAC;AAC3B,CAAC,CAAA;AAGM,SAAS,aAAa,KAAA,EAAqC;AAChE,EAAA,IAAI,IAAA;AACJ,EAAA,IAAI;AACF,IAAA,IAAA,GAAO,OAAO,IAAA,CAAK,KAAA,EAAO,WAAW,CAAA,CAAE,SAAS,MAAM,CAAA;AAAA,EACxD,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,IAAI;AACF,IAAA,OAAO,mBAAA,CAAoB,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,IAAI,CAAC,CAAA;AAAA,EACnD,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAOO,SAAS,QAAA,CAAY,KAAA,EAAqB,MAAA,EAAoB,IAAA,EAAgC;AACnG,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,GAAA,CAAI,MAAA,CAAO,KAAA,IAAS,EAAA,EAAI,CAAC,CAAA,EAAG,IAAA,CAAK,QAAQ,CAAA;AACrE,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,IAAI,OAAO,MAAA,EAAQ;AACjB,IAAA,MAAM,OAAA,GAAU,YAAA,CAAa,MAAA,CAAO,MAAM,CAAA;AAC1C,IAAA,IAAI,OAAA,IAAW,QAAQ,CAAA,IAAK,CAAA,IAAK,QAAQ,CAAA,IAAK,KAAA,CAAM,MAAA,EAAQ,KAAA,GAAQ,OAAA,CAAQ,CAAA;AAAA,EAC9E;AACA,EAAA,MAAM,MAAM,KAAA,GAAQ,KAAA;AACpB,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,KAAA,EAAO,GAAG,CAAA;AACpC,EAAA,MAAM,KAAA,GAAQ,CAAC,GAAA,KAAwC;AACrD,IAAA,MAAM,IAAA,GAAO,MAAM,GAAG,CAAA;AACtB,IAAA,OAAO,KAAK,KAAA,IAAS,IAAA,KAAS,SAAY,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA,GAAI,IAAA;AAAA,EAC/D,CAAA;AACA,EAAA,OAAO;AAAA,IACL,KAAA,EAAO,KAAA;AAAA,IACP,GAAI,GAAA,GAAM,KAAA,CAAM,MAAA,GAAS,EAAE,YAAY,YAAA,CAAa,EAAE,CAAA,EAAG,KAAA,CAAM,GAAG,CAAA,EAAG,CAAA,EAAG,KAAK,CAAA,KAAM,EAAC;AAAA,IACpF,OAAO,KAAA,CAAM;AAAA,GACf;AACF;AASO,SAAS,QAAA,CAA2B,MAAe,MAAA,EAA6C;AACrG,EAAA,MAAM,SAAS,MAAA,EAAQ,MAAA;AACvB,EAAA,MAAM,QACJ,MAAA,KAAW,MAAA,GACP,IAAA,CAAK,KAAA,GACL,KAAK,KAAA,CAAM,GAAA;AAAA,IAAI,UACb,MAAA,CAAO,WAAA;AAAA,MACL,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAA,CAAE,MAAA,CAAO,CAAC,CAAC,GAAG,CAAA,KAAM,MAAA,CAAO,QAAA,CAAS,GAAG,CAAC;AAAA;AAC7D,GACF;AACN,EAAA,OAAO,KAAK,SAAA,CAAU,EAAE,GAAG,IAAA,EAAM,OAAO,CAAA;AAC1C;AAGO,IAAM,eAAA,GAAkB;AAAA,EAC7B,KAAA,EAAO,CAAA,CAAE,MAAA,EAAO,CAAE,KAAI,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,IAAI,GAAG,CAAA,CAAE,QAAA,EAAS,CAAE,SAAS,iCAAiC,CAAA;AAAA,EAC7F,QAAQ,CAAA,CAAE,MAAA,GAAS,QAAA,EAAS,CAAE,SAAS,gDAAgD,CAAA;AAAA,EACvF,OAAA,EAAS,CAAA,CACN,OAAA,EAAQ,CACR,UAAS,CACT,QAAA;AAAA,IACC;AAAA,GAEF;AAAA,EACF,IAAA,EAAM,CAAA,CACH,OAAA,EAAQ,CACR,UAAS,CACT,QAAA;AAAA,IACC;AAAA,GAEF;AAAA,EACF,MAAA,EAAQ,CAAA,CAAE,KAAA,CAAM,CAAA,CAAE,MAAA,EAAQ,CAAA,CAAE,QAAA,EAAS,CAAE,QAAA,CAAS,kCAAkC;AACpF;;;ACrGA,SAAS,WAAW,IAAA,EAA6B;AAC/C,EAAA,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM,MAAA,EAAQ,IAAA,EAAM,CAAA,EAAE;AAC7C;AAkDO,SAAS,UACd,IAAA,EAC2D;AAC3D,EAAA,MAAM,EAAE,SAAS,KAAA,EAAO,QAAA,GAAW,KAAK,OAAA,GAAU,OAAA,EAAS,cAAa,GAAI,IAAA;AAC5E,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,WAAA;AAAA,IACN,WAAW,CAAC,KAAA,MAAqC,EAAE,GAAG,KAAA,EAAO,GAAG,eAAA,EAAgB,CAAA;AAAA,IAChF,WAAA,EAAa,CAAA,OAAA,KAAW,OAAM,KAAA,KAAS;AACrC,MAAA,MAAM,MAAA,GAAU,SAAS,EAAC;AAC1B,MAAA,MAAM,KAAA,GAAS,MAAM,OAAA,CAAQ,KAAK,CAAA;AAClC,MAAA,MAAM,IAAA,GAAO,OAAO,IAAA,KAAS,IAAA;AAC7B,MAAA,MAAM,OAAA,GAAU,IAAA,GAAO,KAAA,GAAQ,MAAA,CAAO,OAAA,KAAY,KAAA;AAClD,MAAA,IAAI,MAAA,CAAO,KAAA,KAAU,MAAA,IAAa,MAAA,CAAO,WAAW,MAAA,EAAW;AAC7D,QAAA,MAAM,OAAO,QAAA,CAAS,KAAA,EAAO,QAAQ,EAAE,QAAA,EAAU,OAAO,CAAA;AACxD,QAAA,MAAM,cAAc,OAAA,KAAY,MAAA,CAAO,MAAA,KAAW,MAAA,IAAa,OAAO,OAAA,KAAY,IAAA,CAAA;AAClF,QAAA,OAAO,WAAA,GACH,WAAW,QAAA,CAAS,EAAE,GAAG,IAAA,EAAM,KAAA,EAAO,KAAK,KAAA,CAAM,GAAA,CAAI,OAAO,CAAA,EAAE,EAAG,MAAM,CAAC,CAAA,GACxE,WAAW,QAAA,CAAS,IAAA,EAAM,MAAM,CAAC,CAAA;AAAA,MACvC;AACA,MAAA,IAAI,YAAA,KAAiB,MAAA,IAAa,KAAA,CAAM,MAAA,GAAS,YAAA,EAAc;AAC7D,QAAA,MAAM,IAAA,GAAO,QAAA,CAAS,KAAA,EAAO,EAAE,OAAO,YAAA,EAAa,EAAG,EAAE,QAAA,EAAU,KAAK,GAAA,CAAI,QAAA,EAAU,YAAY,CAAA,EAAG,OAAO,CAAA;AAC3G,QAAA,MAAMA,QAAO,OAAA,GAAU,IAAA,CAAK,MAAM,GAAA,CAAI,OAAO,IAAI,IAAA,CAAK,KAAA;AACtD,QAAA,OAAO,UAAA;AAAA,UACL,IAAA,CAAK,SAAA,CAAU,EAAE,CAAC,OAAO,GAAGA,KAAAA,EAAM,KAAA,EAAO,IAAA,CAAK,OAAO,SAAA,EAAW,IAAA,EAAM,UAAA,EAAY,IAAA,CAAK,YAAY;AAAA,SACrG;AAAA,MACF;AACA,MAAA,MAAM,IAAA,GAAO,OAAA,GAAU,KAAA,CAAM,GAAA,CAAI,OAAO,CAAA,GAAI,KAAA;AAC5C,MAAA,OAAO,UAAA,CAAW,KAAK,SAAA,CAAU,EAAE,CAAC,OAAO,GAAG,IAAA,EAAM,CAAC,CAAA;AAAA,IACvD;AAAA,GACF;AACF;AAQO,SAAS,WAAA,GAAgE;AAC9E,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,aAAA;AAAA,IACN,WAAA,EAAa,CAAA,OAAA,KAAW,OAAM,KAAA,KAAS;AACrC,MAAA,IAAI;AACF,QAAA,OAAQ,MAAM,QAAQ,KAAK,CAAA;AAAA,MAC7B,SAAS,GAAA,EAAK;AACZ,QAAA,MAAM,OAAO,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,OAAO,GAAG,CAAA;AAC5D,QAAA,OAAO,EAAE,OAAA,EAAS,CAAC,EAAE,IAAA,EAAM,QAAQ,IAAA,EAAM,CAAA,EAAG,OAAA,EAAS,IAAA,EAAK;AAAA,MAC5D;AAAA,IACF;AAAA,GACF;AACF;;;AC9GO,IAAM,SAAA,GAAY;AAClB,IAAM,YAAA,GAAe","file":"index.mjs","sourcesContent":["/**\n * `createTool(params, opts)` — write a TOOL.md to disk.\n *\n * Symmetric to `parseToolManifest` (.md → handle) and `defineTool`\n * (params → handle): this takes params, validates via defineTool\n * (single source of truth — same zod schema, same cross-AIP\n * invariants), then serialises the validated frontmatter to YAML\n * and writes it under `<dir>/<id>/TOOL.md`.\n *\n * The author's body markdown is preserved verbatim in `opts.body`.\n * Pass `dryRun: true` to get the rendered string without touching\n * disk — useful for tests, the MCP server's preview, and CI lints.\n *\n * Non-frontmatter values that can't appear in YAML (zod schemas,\n * function bodies) are filtered out by `filterSerializable` from\n * `@agentproto/define-doctype`; the resulting frontmatter matches\n * what `parseToolManifest` would consume on the way back in.\n */\n\nimport { filterSerializable } from \"@agentproto/define-doctype\"\nimport matter from \"gray-matter\"\nimport { mkdir, writeFile } from \"node:fs/promises\"\nimport { dirname, join } from \"node:path\"\nimport { defineTool } from \"./define-tool.js\"\nimport type { ToolContext, ToolDefinition, ToolHandle } from \"./types.js\"\n\nfunction toSnakeCaseKeys(obj: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(obj).map(([k, v]) => [k.replace(/([A-Z])/g, \"_$1\").toLowerCase(), v]),\n )\n}\n\nexport interface CreateToolOptions {\n /** Workspace-relative or absolute directory under which to write `<id>/TOOL.md`. */\n dir: string\n /** Body markdown after the frontmatter. Defaults to a one-line stub. */\n body?: string\n /** Render only — don't write. Returns the rendered string. */\n dryRun?: boolean\n}\n\nexport interface CreateToolResult<\n TInput,\n TOutput,\n TContext extends ToolContext,\n> {\n /** The path where the file was (or would be) written. */\n path: string\n /** The validated handle returned by defineTool. */\n handle: ToolHandle<TInput, TOutput, TContext>\n /** The full rendered file contents (frontmatter + body). */\n rendered: string\n}\n\nexport async function createTool<\n TInput,\n TOutput,\n TContext extends ToolContext = ToolContext,\n>(\n params: ToolDefinition<TInput, TOutput, TContext>,\n opts: CreateToolOptions,\n): Promise<CreateToolResult<TInput, TOutput, TContext>> {\n const handle = defineTool<TInput, TOutput, TContext>(params)\n const path = join(opts.dir, handle.id, \"TOOL.md\")\n const frontmatter = filterSerializable({\n schema: \"agentproto/tool/v1\",\n ...toSnakeCaseKeys(params as unknown as Record<string, unknown>),\n }) as Record<string, unknown>\n const rendered = matter.stringify(\n opts.body ?? `# ${handle.name}\\n\\n${handle.description}\\n`,\n frontmatter,\n )\n if (!opts.dryRun) {\n await mkdir(dirname(path), { recursive: true })\n await writeFile(path, rendered, \"utf8\")\n }\n return { path, handle, rendered }\n}\n","/**\n * AIP-14 doctype spec — fed to `@agentproto/manifest.createVerbs`\n * to derive create / load / list / update / resolve / delete in one\n * place. Keeping the spec separate from `define-tool.ts` lets the\n * manifest layer (and the future MCP server) iterate over a uniform\n * descriptor without each package re-implementing the wiring.\n */\n\nimport { createVerbs, type DoctypeSpec } from \"@agentproto/manifest\"\nimport { defineTool } from \"./define-tool.js\"\nimport { parseToolManifest } from \"./manifest/index.js\"\nimport type { ToolDefinition, ToolHandle } from \"./types.js\"\n\nexport const toolSpec: DoctypeSpec<\n ToolDefinition<unknown, unknown>,\n ToolHandle<unknown, unknown>\n> = {\n name: \"tool\",\n aip: 14,\n schemaLiteral: \"agentproto/tool/v1\",\n pathOf: (h) => `${h.id}/TOOL.md`,\n define: (params) =>\n defineTool(params) as ToolHandle<unknown, unknown>,\n parse: (source) => {\n const m = parseToolManifest(source)\n const fm = m.frontmatter\n // `createVerbs.load` feeds this frontmatter straight to `define`\n // (= `defineTool`), which reads camelCase keys. The manifest uses\n // snake_case for multi-word fields, so they must be renamed here or\n // `defineTool` silently falls back to its defaults. Single-word fields\n // (mutates, approval, idempotent, tags, metadata, requires) share the\n // same key in both conventions and pass through unchanged.\n const frontmatter: Record<string, unknown> = {\n id: fm.id,\n name: fm.name,\n description: fm.description,\n version: fm.version,\n ...(fm.mutates !== undefined && { mutates: fm.mutates }),\n ...(fm.requires !== undefined && { requires: fm.requires }),\n ...(fm.approval !== undefined && { approval: fm.approval }),\n ...(fm.risk_level !== undefined && { riskLevel: fm.risk_level }),\n ...(fm.cost_class !== undefined && { costClass: fm.cost_class }),\n ...(fm.timeout_ms !== undefined && { timeoutMs: fm.timeout_ms }),\n ...(fm.idempotent !== undefined && { idempotent: fm.idempotent }),\n ...(fm.tags !== undefined && { tags: fm.tags }),\n ...(fm.metadata !== undefined && { metadata: fm.metadata }),\n // AIP-16 IO blocks pass through under the same keys.\n ...(fm.inputs !== undefined && { inputs: fm.inputs }),\n ...(fm.outputs !== undefined && { outputs: fm.outputs }),\n }\n return { frontmatter, body: m.body }\n },\n}\n\nexport const toolVerbs = createVerbs(toolSpec)\n","import { z } from \"zod\"\n\n/**\n * Shared pagination primitives (moved here from `@agentproto/runtime`'s\n * `tool-envelope.ts` so the `paginated()` ToolTransformer can reuse the\n * exact cursor/limit semantics the daemon's list tools already depend on).\n * `@agentproto/runtime` re-exports this module verbatim, so existing\n * imports are unchanged.\n */\n\n/** Shared pagination params, spread into every list tool's input schema. */\nexport interface PageParams {\n limit?: number\n cursor?: string\n since?: number\n compact?: boolean\n full?: boolean\n fields?: string[]\n}\n\nexport interface Page<T> {\n items: T[]\n nextCursor?: string\n total?: number\n truncated?: boolean\n}\n\n/** Opaque cursor: base64(JSON `{ k: keysetValue, i: index }`). */\nexport interface CursorPayload {\n k: string | number | null\n i: number\n}\n\nexport function encodeCursor(payload: CursorPayload): string {\n return Buffer.from(JSON.stringify(payload), \"utf8\").toString(\"base64url\")\n}\n\nconst cursorPayloadSchema = z.object({\n k: z.union([z.string(), z.number(), z.null()]),\n i: z.number().int().min(0),\n})\n\n/** No `unknown`/`as`: the JSON boundary is typed by zod's safeParse/parse. */\nexport function decodeCursor(token: string): CursorPayload | null {\n let text: string\n try {\n text = Buffer.from(token, \"base64url\").toString(\"utf8\")\n } catch {\n return null\n }\n try {\n return cursorPayloadSchema.parse(JSON.parse(text))\n } catch {\n return null\n }\n}\n\nexport interface PaginateOpts<T> {\n maxLimit: number\n keyOf?: (item: T) => string | number | null\n}\n\nexport function paginate<T>(items: readonly T[], params: PageParams, opts: PaginateOpts<T>): Page<T> {\n const limit = Math.min(Math.max(params.limit ?? 50, 1), opts.maxLimit)\n let start = 0\n if (params.cursor) {\n const decoded = decodeCursor(params.cursor)\n if (decoded && decoded.i >= 0 && decoded.i <= items.length) start = decoded.i\n }\n const end = start + limit\n const slice = items.slice(start, end)\n const keyAt = (idx: number): string | number | null => {\n const item = items[idx]\n return opts.keyOf && item !== undefined ? opts.keyOf(item) : null\n }\n return {\n items: slice,\n ...(end < items.length ? { nextCursor: encodeCursor({ k: keyAt(end), i: end }) } : {}),\n total: items.length,\n }\n}\n\n/**\n * Compact serializer — replaces every `JSON.stringify(x, null, 2)` in list handlers.\n *\n * When `params.fields` is supplied, an explicit allowlist is applied to every\n * item (mechanically, across all call sites): items keep only the named keys.\n * Absent `fields`, the output is byte-identical to `JSON.stringify(page)`.\n */\nexport function toolText<T extends object>(page: Page<T>, params?: Pick<PageParams, \"fields\">): string {\n const fields = params?.fields\n const items =\n fields === undefined\n ? page.items\n : page.items.map(item =>\n Object.fromEntries(\n Object.entries(item).filter(([key]) => fields.includes(key)),\n ),\n )\n return JSON.stringify({ ...page, items })\n}\n\n/** Zod fragment to spread into each list tool's input schema. */\nexport const pageParamsShape = {\n limit: z.number().int().min(1).max(200).optional().describe(\"Max items per page. Default 50.\"),\n cursor: z.string().optional().describe(\"Opaque token from a prior call's `nextCursor`.\"),\n compact: z\n .boolean()\n .optional()\n .describe(\n \"Compact projection (fewer fields per item). Default true on tools \" +\n \"that define a compact projection (session_list); a no-op elsewhere.\",\n ),\n full: z\n .boolean()\n .optional()\n .describe(\n \"Escape hatch — full, unprojected records (opts out of compact). \" +\n \"Default false.\",\n ),\n fields: z.array(z.string()).optional().describe(\"Keep only these fields per item.\"),\n} as const\n","import type { ZodRawShape } from \"zod\"\nimport {\n paginate,\n pageParamsShape,\n toolText,\n type PageParams,\n} from \"./envelope.js\"\nimport type { ToolTransformer } from \"./types.js\"\n\n/**\n * Concrete {@link ToolTransformer}s — cross-cutting concerns composed by\n * MCP-facing adapters (`toMcpTool`) around a tool's handler pipeline.\n */\n\n/** The MCP text-result shape transformers terminate the pipeline into. */\nexport interface McpTextResult {\n content: Array<{ type: \"text\"; text: string }>\n isError?: boolean\n}\n\nfunction textResult(text: string): McpTextResult {\n return { content: [{ type: \"text\", text }] }\n}\n\nexport interface PaginatedOptions<TItem> {\n /**\n * REQUIRED per-item compact projection. Requiring it is the point: there\n * is no code path that accepts the `compact` param without an actual\n * compact projection behind it, killing the \"declared but dead param\"\n * bug class. Pass the identity function only if you truly want\n * `compact` to be a no-op.\n */\n project: (item: TItem) => object\n /** Key function for the opaque pagination cursor. */\n keyOf?: (item: TItem) => string | number | null\n /** Hard ceiling on `limit` (default 200 — matches the daemon tools). */\n maxLimit?: number\n /**\n * Wrapper key for the NON-paginated output. With no `limit`/`cursor`,\n * legacy list tools returned `{ sessions: [...] }` rather than the page\n * envelope; set this to reproduce that wrapper (default `\"items\"`).\n */\n itemKey?: string\n /**\n * Row cap for the NON-paginated (no `limit`/`cursor`) call. Unset = the\n * legacy unbounded `{ [itemKey]: [...] }`. When set and the list is longer,\n * the wrapper carries the first `defaultLimit` rows plus\n * `{ total, truncated: true, nextCursor }` so the caller can page on —\n * keeps a tool whose rows pile up (terminals) under the MCP output cap.\n */\n defaultLimit?: number\n}\n\n/**\n * Pagination transformer — adds {@link pageParamsShape} to the input shape\n * and wraps the handler (which must return the FULL, unprojected item\n * array) with the shared cursor/limit semantics:\n *\n * - `limit`/`cursor` present → the `{ items, nextCursor?, total }` page\n * envelope via `paginate` + `toolText` (cursor/limit semantics are the\n * exact ones the daemon's list tools already depend on).\n * - neither present → the legacy `{ [itemKey]: [...] }` wrapper, no\n * envelope fields.\n *\n * Rows are COMPACT by default (`project`); `full: true` / `compact: false`\n * returns the unprojected records. `fields` is a per-item allowlist\n * applied on the paginated envelope branch (matching existing behavior).\n * An explicit `fields` list is itself a projection, bounded by what the\n * caller names, so on that branch it is applied to the FULL record and the\n * compact projection is skipped (unless `compact: true` is explicit) —\n * otherwise a requested field outside the compact set is silently dropped.\n */\nexport function paginated<TItem extends object>(\n opts: PaginatedOptions<TItem>,\n): ToolTransformer<unknown, readonly TItem[], McpTextResult> {\n const { project, keyOf, maxLimit = 200, itemKey = \"items\", defaultLimit } = opts\n return {\n name: \"paginated\",\n wrapShape: (shape: ZodRawShape): ZodRawShape => ({ ...shape, ...pageParamsShape }),\n wrapHandler: handler => async input => {\n const params = (input ?? {}) as PageParams\n const items = (await handler(input)) as readonly TItem[]\n const full = params.full === true\n const compact = full ? false : params.compact !== false\n if (params.limit !== undefined || params.cursor !== undefined) {\n const page = paginate(items, params, { maxLimit, keyOf })\n const projectRows = compact && (params.fields === undefined || params.compact === true)\n return projectRows\n ? textResult(toolText({ ...page, items: page.items.map(project) }, params))\n : textResult(toolText(page, params))\n }\n if (defaultLimit !== undefined && items.length > defaultLimit) {\n const page = paginate(items, { limit: defaultLimit }, { maxLimit: Math.max(maxLimit, defaultLimit), keyOf })\n const rows = compact ? page.items.map(project) : page.items\n return textResult(\n JSON.stringify({ [itemKey]: rows, total: page.total, truncated: true, nextCursor: page.nextCursor }),\n )\n }\n const rows = compact ? items.map(project) : items\n return textResult(JSON.stringify({ [itemKey]: rows }))\n },\n }\n}\n\n/**\n * Error-normalization transformer — wraps the handler so ANY thrown error\n * becomes the single canonical MCP error result\n * `{ content: [{ type: \"text\", text: <message> }], isError: true }`,\n * replacing the ~150 copy-pasted inline catch blocks.\n */\nexport function catchErrors(): ToolTransformer<unknown, unknown, McpTextResult> {\n return {\n name: \"catchErrors\",\n wrapHandler: handler => async input => {\n try {\n return (await handler(input)) as McpTextResult\n } catch (err) {\n const text = err instanceof Error ? err.message : String(err)\n return { content: [{ type: \"text\", text }], isError: true }\n }\n },\n }\n}\n","/**\n * @agentproto/tool — AIP-14 TOOL.md `defineTool` reference impl.\n *\n * Vendor-neutral tool **contract** registration: an author writes\n * `defineTool({...})` and the runtime returns a `ToolHandle` carrying\n * identity, schemas, side-effect profile, approval class, and\n * provider routing hints. Bodies live on AIP-30 PROVIDER manifests;\n * invocation goes through `@agentproto/driver`.\n *\n * Spec: https://agentproto.sh/docs/aip-14\n */\n\nexport const SPEC_NAME = \"agenttool/v1\" as const\nexport const SPEC_VERSION = \"1.0.0-alpha\" as const\n\nexport {\n defineTool,\n validateInput,\n validateContext,\n validateOutput,\n} from \"./define-tool.js\"\n// Standalone create-tool kept for back-compat; new code should import\n// the unified verb surface (`toolVerbs.create`, `.load`, `.list`,\n// `.update`, `.resolve`, `.delete`) from `./spec.js`.\nexport {\n createTool,\n type CreateToolOptions,\n type CreateToolResult,\n} from \"./create-tool.js\"\nexport { toolSpec, toolVerbs } from \"./spec.js\"\nexport {\n parseToolManifest,\n toolFromManifest,\n toolFromManifestOnly,\n type ToolManifest,\n type ToolManifestFrontmatter,\n} from \"./manifest/index.js\"\nexport {\n checkMinimalJsonSchema,\n validateManifestIOBlocks,\n type MinimalJsonSchemaIssue,\n} from \"./json-schema.js\"\nexport {\n ToolError,\n toToolError,\n toToolResult,\n type ToolErrorCode,\n type ToolErrorPayload,\n} from \"./errors.js\"\nexport {\n paginated,\n catchErrors,\n type McpTextResult,\n type PaginatedOptions,\n} from \"./transformers.js\"\nexport {\n paginate,\n toolText,\n encodeCursor,\n decodeCursor,\n pageParamsShape,\n type Page,\n type PageParams,\n type PaginateOpts,\n type CursorPayload,\n} from \"./envelope.js\"\nexport type {\n ToolDefinition,\n ToolHandle,\n ToolTransformer,\n ToolContext,\n ToolCapabilities,\n ApprovalClass,\n DriverConstraints,\n DriverKind,\n RetryPolicy,\n ToolResult,\n ValidationResult,\n ValidationFailure,\n ValidationSuccess,\n} from \"./types.js\"\n"]}
1
+ {"version":3,"sources":["../src/create-tool.ts","../src/spec.ts","../src/envelope.ts","../src/transformers.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * `createTool(params, opts)` — write a TOOL.md to disk.\n *\n * Symmetric to `parseToolManifest` (.md → handle) and `defineTool`\n * (params → handle): this takes params, validates via defineTool\n * (single source of truth — same zod schema, same cross-AIP\n * invariants), then serialises the validated frontmatter to YAML\n * and writes it under `<dir>/<id>/TOOL.md`.\n *\n * The author's body markdown is preserved verbatim in `opts.body`.\n * Pass `dryRun: true` to get the rendered string without touching\n * disk — useful for tests, the MCP server's preview, and CI lints.\n *\n * Non-frontmatter values that can't appear in YAML (zod schemas,\n * function bodies) are filtered out by `filterSerializable` from\n * `@agentproto/define-doctype`; the resulting frontmatter matches\n * what `parseToolManifest` would consume on the way back in.\n */\n\nimport { filterSerializable } from \"@agentproto/define-doctype\"\nimport matter from \"gray-matter\"\nimport { mkdir, writeFile } from \"node:fs/promises\"\nimport { dirname, join } from \"node:path\"\nimport { defineTool } from \"./define-tool.js\"\nimport type { ToolContext, ToolDefinition, ToolHandle } from \"./types.js\"\n\nfunction toSnakeCaseKeys(obj: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(obj).map(([k, v]) => [k.replace(/([A-Z])/g, \"_$1\").toLowerCase(), v]),\n )\n}\n\nexport interface CreateToolOptions {\n /** Workspace-relative or absolute directory under which to write `<id>/TOOL.md`. */\n dir: string\n /** Body markdown after the frontmatter. Defaults to a one-line stub. */\n body?: string\n /** Render only — don't write. Returns the rendered string. */\n dryRun?: boolean\n}\n\nexport interface CreateToolResult<\n TInput,\n TOutput,\n TContext extends ToolContext,\n> {\n /** The path where the file was (or would be) written. */\n path: string\n /** The validated handle returned by defineTool. */\n handle: ToolHandle<TInput, TOutput, TContext>\n /** The full rendered file contents (frontmatter + body). */\n rendered: string\n}\n\nexport async function createTool<\n TInput,\n TOutput,\n TContext extends ToolContext = ToolContext,\n>(\n params: ToolDefinition<TInput, TOutput, TContext>,\n opts: CreateToolOptions,\n): Promise<CreateToolResult<TInput, TOutput, TContext>> {\n const handle = defineTool<TInput, TOutput, TContext>(params)\n const path = join(opts.dir, handle.id, \"TOOL.md\")\n const frontmatter = filterSerializable({\n schema: \"agentproto/tool/v1\",\n ...toSnakeCaseKeys(params as unknown as Record<string, unknown>),\n }) as Record<string, unknown>\n const rendered = matter.stringify(\n opts.body ?? `# ${handle.name}\\n\\n${handle.description}\\n`,\n frontmatter,\n )\n if (!opts.dryRun) {\n await mkdir(dirname(path), { recursive: true })\n await writeFile(path, rendered, \"utf8\")\n }\n return { path, handle, rendered }\n}\n","/**\n * AIP-14 doctype spec — fed to `@agentproto/manifest.createVerbs`\n * to derive create / load / list / update / resolve / delete in one\n * place. Keeping the spec separate from `define-tool.ts` lets the\n * manifest layer (and the future MCP server) iterate over a uniform\n * descriptor without each package re-implementing the wiring.\n */\n\nimport { createVerbs, type DoctypeSpec } from \"@agentproto/manifest\"\nimport { defineTool } from \"./define-tool.js\"\nimport { parseToolManifest } from \"./manifest/index.js\"\nimport type { ToolDefinition, ToolHandle } from \"./types.js\"\n\nexport const toolSpec: DoctypeSpec<\n ToolDefinition<unknown, unknown>,\n ToolHandle<unknown, unknown>\n> = {\n name: \"tool\",\n aip: 14,\n schemaLiteral: \"agentproto/tool/v1\",\n pathOf: (h) => `${h.id}/TOOL.md`,\n define: (params) =>\n defineTool(params) as ToolHandle<unknown, unknown>,\n parse: (source) => {\n const m = parseToolManifest(source)\n const fm = m.frontmatter\n // `createVerbs.load` feeds this frontmatter straight to `define`\n // (= `defineTool`), which reads camelCase keys. The manifest uses\n // snake_case for multi-word fields, so they must be renamed here or\n // `defineTool` silently falls back to its defaults. Single-word fields\n // (mutates, approval, idempotent, tags, metadata, requires) share the\n // same key in both conventions and pass through unchanged.\n const frontmatter: Record<string, unknown> = {\n id: fm.id,\n name: fm.name,\n description: fm.description,\n version: fm.version,\n ...(fm.mutates !== undefined && { mutates: fm.mutates }),\n ...(fm.requires !== undefined && { requires: fm.requires }),\n ...(fm.approval !== undefined && { approval: fm.approval }),\n ...(fm.risk_level !== undefined && { riskLevel: fm.risk_level }),\n ...(fm.cost_class !== undefined && { costClass: fm.cost_class }),\n ...(fm.timeout_ms !== undefined && { timeoutMs: fm.timeout_ms }),\n ...(fm.idempotent !== undefined && { idempotent: fm.idempotent }),\n ...(fm.tags !== undefined && { tags: fm.tags }),\n ...(fm.metadata !== undefined && { metadata: fm.metadata }),\n // AIP-16 IO blocks pass through under the same keys.\n ...(fm.inputs !== undefined && { inputs: fm.inputs }),\n ...(fm.outputs !== undefined && { outputs: fm.outputs }),\n }\n return { frontmatter, body: m.body }\n },\n}\n\nexport const toolVerbs = createVerbs(toolSpec)\n","import { z } from \"zod\"\n\n/**\n * Shared pagination primitives (moved here from `@agentproto/runtime`'s\n * `tool-envelope.ts` so the `paginated()` ToolTransformer can reuse the\n * exact cursor/limit semantics the daemon's list tools already depend on).\n * `@agentproto/runtime` re-exports this module verbatim, so existing\n * imports are unchanged.\n */\n\n/** Shared pagination params, spread into every list tool's input schema. */\nexport interface PageParams {\n limit?: number\n cursor?: string\n since?: number\n compact?: boolean\n full?: boolean\n fields?: string[]\n}\n\nexport interface Page<T> {\n items: T[]\n nextCursor?: string\n total?: number\n truncated?: boolean\n}\n\n/** Opaque cursor: base64(JSON `{ k: keysetValue, i: index }`). */\nexport interface CursorPayload {\n k: string | number | null\n i: number\n}\n\nexport function encodeCursor(payload: CursorPayload): string {\n return Buffer.from(JSON.stringify(payload), \"utf8\").toString(\"base64url\")\n}\n\nconst cursorPayloadSchema = z.object({\n k: z.union([z.string(), z.number(), z.null()]),\n i: z.number().int().min(0),\n})\n\n/** No `unknown`/`as`: the JSON boundary is typed by zod's safeParse/parse. */\nexport function decodeCursor(token: string): CursorPayload | null {\n let text: string\n try {\n text = Buffer.from(token, \"base64url\").toString(\"utf8\")\n } catch {\n return null\n }\n try {\n return cursorPayloadSchema.parse(JSON.parse(text))\n } catch {\n return null\n }\n}\n\nexport interface PaginateOpts<T> {\n maxLimit: number\n keyOf?: (item: T) => string | number | null\n}\n\nexport function paginate<T>(items: readonly T[], params: PageParams, opts: PaginateOpts<T>): Page<T> {\n const limit = Math.min(Math.max(params.limit ?? 50, 1), opts.maxLimit)\n let start = 0\n if (params.cursor) {\n const decoded = decodeCursor(params.cursor)\n if (decoded && decoded.i >= 0 && decoded.i <= items.length) start = decoded.i\n }\n const end = start + limit\n const slice = items.slice(start, end)\n const keyAt = (idx: number): string | number | null => {\n const item = items[idx]\n return opts.keyOf && item !== undefined ? opts.keyOf(item) : null\n }\n return {\n items: slice,\n ...(end < items.length ? { nextCursor: encodeCursor({ k: keyAt(end), i: end }) } : {}),\n total: items.length,\n }\n}\n\n/**\n * Compact serializer — replaces every `JSON.stringify(x, null, 2)` in list handlers.\n *\n * When `params.fields` is supplied, an explicit allowlist is applied to every\n * item (mechanically, across all call sites): items keep only the named keys.\n * Absent `fields`, the output is byte-identical to `JSON.stringify(page)`.\n */\nexport function toolText<T extends object>(page: Page<T>, params?: Pick<PageParams, \"fields\">): string {\n const fields = params?.fields\n const items =\n fields === undefined\n ? page.items\n : page.items.map(item =>\n Object.fromEntries(\n Object.entries(item).filter(([key]) => fields.includes(key)),\n ),\n )\n return JSON.stringify({ ...page, items })\n}\n\n/** Zod fragment to spread into each list tool's input schema. */\nexport const pageParamsShape = {\n limit: z.number().int().min(1).max(200).optional().describe(\"Max items per page. Default 50.\"),\n cursor: z.string().optional().describe(\"Opaque token from a prior call's `nextCursor`.\"),\n compact: z\n .boolean()\n .optional()\n .describe(\n \"Compact projection (fewer fields per item). Default true on tools \" +\n \"that define a compact projection (session_list); a no-op elsewhere.\",\n ),\n full: z\n .boolean()\n .optional()\n .describe(\n \"Escape hatch — full, unprojected records (opts out of compact). \" +\n \"Default false.\",\n ),\n fields: z.array(z.string()).optional().describe(\"Keep only these fields per item.\"),\n} as const\n","import type { ZodRawShape } from \"zod\"\nimport {\n paginate,\n pageParamsShape,\n type PageParams,\n} from \"./envelope.js\"\nimport type { ToolTransformer } from \"./types.js\"\n\n/**\n * Concrete {@link ToolTransformer}s — cross-cutting concerns composed by\n * MCP-facing adapters (`toMcpTool`) around a tool's handler pipeline.\n */\n\n/** The MCP text-result shape transformers terminate the pipeline into. */\nexport interface McpTextResult {\n content: Array<{ type: \"text\"; text: string }>\n isError?: boolean\n}\n\nfunction textResult(text: string): McpTextResult {\n return { content: [{ type: \"text\", text }] }\n}\n\nexport interface PaginatedOptions<TItem> {\n /**\n * REQUIRED per-item compact projection. Requiring it is the point: there\n * is no code path that accepts the `compact` param without an actual\n * compact projection behind it, killing the \"declared but dead param\"\n * bug class. Pass the identity function only if you truly want\n * `compact` to be a no-op.\n */\n project: (item: TItem) => object\n /** Key function for the opaque pagination cursor. */\n keyOf?: (item: TItem) => string | number | null\n /** Hard ceiling on `limit` (default 200 — matches the daemon tools). */\n maxLimit?: number\n /**\n * Wrapper key for the NON-paginated output. With no `limit`/`cursor`,\n * legacy list tools returned `{ sessions: [...] }` rather than the page\n * envelope; set this to reproduce that wrapper (default `\"items\"`).\n */\n itemKey?: string\n /**\n * Row cap for the NON-paginated (no `limit`/`cursor`) call. Unset = the\n * legacy unbounded `{ [itemKey]: [...] }`. When set and the list is longer,\n * the wrapper carries the first `defaultLimit` rows plus\n * `{ total, truncated: true, nextCursor }` so the caller can page on —\n * keeps a tool whose rows pile up (terminals) under the MCP output cap.\n */\n defaultLimit?: number\n /**\n * Add `total` (the size of the list the handler returned, i.e. after any\n * filtering) to the NON-paginated wrapper too. Default off: the legacy\n * `{ [itemKey]: [...] }` shape stays byte-identical for existing tools.\n */\n includeTotal?: boolean\n}\n\n/**\n * Pagination transformer — adds {@link pageParamsShape} to the input shape\n * and wraps the handler (which must return the FULL, unprojected item\n * array) with the shared cursor/limit semantics:\n *\n * - `limit`/`cursor` present → the `{ items, nextCursor?, total }` page\n * envelope via `paginate` (cursor/limit semantics are the\n * exact ones the daemon's list tools already depend on).\n * - neither present → the legacy `{ [itemKey]: [...] }` wrapper, no\n * envelope fields (unless `includeTotal` adds `total`).\n *\n * Rows are COMPACT by default (`project`); `full: true` / `compact: false`\n * returns the unprojected records. `fields` is a per-item allowlist\n * applied on every branch (paginated, truncated and plain wrapper).\n * An explicit `fields` list is itself a projection, bounded by what the\n * caller names, so on that branch it is applied to the FULL record and the\n * compact projection is skipped (unless `compact: true` is explicit) —\n * otherwise a requested field outside the compact set is silently dropped.\n */\nexport function paginated<TItem extends object>(\n opts: PaginatedOptions<TItem>,\n): ToolTransformer<unknown, readonly TItem[], McpTextResult> {\n const { project, keyOf, maxLimit = 200, itemKey = \"items\", defaultLimit, includeTotal } = opts\n return {\n name: \"paginated\",\n wrapShape: (shape: ZodRawShape): ZodRawShape => ({ ...shape, ...pageParamsShape }),\n wrapHandler: handler => async input => {\n const params = (input ?? {}) as PageParams\n const items = (await handler(input)) as readonly TItem[]\n const full = params.full === true\n const compact = full ? false : params.compact !== false\n const projectRows = compact && (params.fields === undefined || params.compact === true)\n const render = (rows: readonly TItem[]): object[] => {\n const projected = projectRows ? rows.map(project) : [...rows]\n const fields = params.fields\n return fields === undefined\n ? projected\n : projected.map(row => Object.fromEntries(Object.entries(row).filter(([key]) => fields.includes(key))))\n }\n if (params.limit !== undefined || params.cursor !== undefined) {\n const page = paginate(items, params, { maxLimit, keyOf })\n return textResult(JSON.stringify({ ...page, items: render(page.items) }))\n }\n if (defaultLimit !== undefined && items.length > defaultLimit) {\n const page = paginate(items, { limit: defaultLimit }, { maxLimit: Math.max(maxLimit, defaultLimit), keyOf })\n return textResult(\n JSON.stringify({\n [itemKey]: render(page.items),\n total: page.total,\n truncated: true,\n nextCursor: page.nextCursor,\n }),\n )\n }\n return textResult(\n JSON.stringify({ [itemKey]: render(items), ...(includeTotal ? { total: items.length } : {}) }),\n )\n },\n }\n}\n\n/**\n * Error-normalization transformer — wraps the handler so ANY thrown error\n * becomes the single canonical MCP error result\n * `{ content: [{ type: \"text\", text: <message> }], isError: true }`,\n * replacing the ~150 copy-pasted inline catch blocks.\n */\nexport function catchErrors(): ToolTransformer<unknown, unknown, McpTextResult> {\n return {\n name: \"catchErrors\",\n wrapHandler: handler => async input => {\n try {\n return (await handler(input)) as McpTextResult\n } catch (err) {\n const text = err instanceof Error ? err.message : String(err)\n return { content: [{ type: \"text\", text }], isError: true }\n }\n },\n }\n}\n","/**\n * @agentproto/tool — AIP-14 TOOL.md `defineTool` reference impl.\n *\n * Vendor-neutral tool **contract** registration: an author writes\n * `defineTool({...})` and the runtime returns a `ToolHandle` carrying\n * identity, schemas, side-effect profile, approval class, and\n * provider routing hints. Bodies live on AIP-30 PROVIDER manifests;\n * invocation goes through `@agentproto/driver`.\n *\n * Spec: https://agentproto.sh/docs/aip-14\n */\n\nexport const SPEC_NAME = \"agenttool/v1\" as const\nexport const SPEC_VERSION = \"1.0.0-alpha\" as const\n\nexport {\n defineTool,\n validateInput,\n validateContext,\n validateOutput,\n} from \"./define-tool.js\"\n// Standalone create-tool kept for back-compat; new code should import\n// the unified verb surface (`toolVerbs.create`, `.load`, `.list`,\n// `.update`, `.resolve`, `.delete`) from `./spec.js`.\nexport {\n createTool,\n type CreateToolOptions,\n type CreateToolResult,\n} from \"./create-tool.js\"\nexport { toolSpec, toolVerbs } from \"./spec.js\"\nexport {\n parseToolManifest,\n toolFromManifest,\n toolFromManifestOnly,\n type ToolManifest,\n type ToolManifestFrontmatter,\n} from \"./manifest/index.js\"\nexport {\n checkMinimalJsonSchema,\n validateManifestIOBlocks,\n type MinimalJsonSchemaIssue,\n} from \"./json-schema.js\"\nexport {\n ToolError,\n toToolError,\n toToolResult,\n type ToolErrorCode,\n type ToolErrorPayload,\n} from \"./errors.js\"\nexport {\n paginated,\n catchErrors,\n type McpTextResult,\n type PaginatedOptions,\n} from \"./transformers.js\"\nexport {\n paginate,\n toolText,\n encodeCursor,\n decodeCursor,\n pageParamsShape,\n type Page,\n type PageParams,\n type PaginateOpts,\n type CursorPayload,\n} from \"./envelope.js\"\nexport type {\n ToolDefinition,\n ToolHandle,\n ToolTransformer,\n ToolContext,\n ToolCapabilities,\n ApprovalClass,\n DriverConstraints,\n DriverKind,\n RetryPolicy,\n ToolResult,\n ValidationResult,\n ValidationFailure,\n ValidationSuccess,\n} from \"./types.js\"\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentproto/tool",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "agentproto/tool-runtime — reference implementation of AIP-14 TOOL.md `defineTool` contract. Vendor-neutral tool registration with validated input/output schemas, structured error envelopes, and side-effect declarations. Per-framework adapters (Mastra, LangChain, A2A) consume the produced ToolHandle.",
5
5
  "keywords": [
6
6
  "agentproto",
@@ -48,12 +48,12 @@
48
48
  "dependencies": {
49
49
  "ajv": "^8.20.0",
50
50
  "gray-matter": "^4.0.3",
51
- "zod": "^4.6.5",
52
- "@agentproto/define-doctype": "0.1.3",
53
- "@agentproto/manifest": "0.2.3"
51
+ "zod": "^4.6.7",
52
+ "@agentproto/define-doctype": "0.1.4",
53
+ "@agentproto/manifest": "0.2.4"
54
54
  },
55
55
  "devDependencies": {
56
- "@types/node": "^25.9.5",
56
+ "@types/node": "^25.9.9",
57
57
  "tsup": "^8.5.1",
58
58
  "typescript": "^5.9.3",
59
59
  "vitest": "^3.2.7",