@feniix/bridgekit 0.5.0 → 0.6.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.
@@ -1 +1,9 @@
1
+ /**
2
+ * Defensively pulls an `AbortSignal` out of MCP request `extra`. The MCP SDK
3
+ * does not formally type a `signal` on `extra`, so the shape is sniffed
4
+ * structurally.
5
+ *
6
+ * Validated against `@modelcontextprotocol/sdk` v1.x. Revalidate on any SDK
7
+ * major bump in case the cancellation channel moves or gains a typed surface.
8
+ */
1
9
  export declare function signalFromExtra(extra: unknown): AbortSignal | undefined;
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Defensively pulls an `AbortSignal` out of MCP request `extra`. The MCP SDK
3
+ * does not formally type a `signal` on `extra`, so the shape is sniffed
4
+ * structurally.
5
+ *
6
+ * Validated against `@modelcontextprotocol/sdk` v1.x. Revalidate on any SDK
7
+ * major bump in case the cancellation channel moves or gains a typed surface.
8
+ */
1
9
  export function signalFromExtra(extra) {
2
10
  if (!extra || typeof extra !== "object" || !("signal" in extra)) {
3
11
  return undefined;
@@ -1,5 +1,5 @@
1
1
  import type { TSchema } from "typebox";
2
- import type { PortableTool, PortableToolResult } from "../core/define-tool.js";
2
+ import type { PortableTool, PortableToolErrorDetails, PortableToolResult } from "../core/define-tool.js";
3
3
  type PiContent = {
4
4
  type: "text";
5
5
  text: string;
@@ -23,7 +23,7 @@ export type PiToolRegistration = {
23
23
  registerTool(tool: PiToolDefinition): unknown;
24
24
  };
25
25
  export declare class PortableToolExecutionError extends Error {
26
- readonly details: Record<string, unknown>;
26
+ readonly details: PortableToolErrorDetails;
27
27
  constructor(result: PortableToolResult);
28
28
  }
29
29
  export declare function isPortableToolExecutionError(error: unknown): error is PortableToolExecutionError;
@@ -2,12 +2,23 @@ import { executePortableTool } from "../core/execute-tool.js";
2
2
  function toPiDetails(result) {
3
3
  return result.structuredContent ?? result.details ?? {};
4
4
  }
5
+ function isValidationDetails(source) {
6
+ return source.kind === "validation" && typeof source.tool === "string" && Array.isArray(source.validationErrors);
7
+ }
8
+ function toPortableToolErrorDetails(result) {
9
+ const source = toPiDetails(result);
10
+ if (isValidationDetails(source)) {
11
+ return source;
12
+ }
13
+ const { kind: _ignored, ...rest } = source;
14
+ return { kind: "domain", ...rest };
15
+ }
5
16
  export class PortableToolExecutionError extends Error {
6
17
  details;
7
18
  constructor(result) {
8
19
  super(result.text);
9
20
  this.name = "PortableToolExecutionError";
10
- this.details = toPiDetails(result);
21
+ this.details = toPortableToolErrorDetails(result);
11
22
  }
12
23
  }
13
24
  export function isPortableToolExecutionError(error) {
@@ -1,14 +1,41 @@
1
1
  import type { Static, TSchema } from "typebox";
2
- export interface PortableToolResult {
2
+ export interface PortableToolResult<TStructured extends Record<string, unknown> = Record<string, unknown>> {
3
3
  /** Plain text sent back to the model in every host. */
4
4
  text: string;
5
5
  /** Structured data for hosts that support it. Preferred by both pi and MCP adapters. */
6
- structuredContent?: Record<string, unknown>;
7
- /** Legacy/adapter debug details used only when structuredContent is absent. */
6
+ structuredContent?: TStructured;
7
+ /**
8
+ * Legacy/adapter debug details used only when `structuredContent` is absent.
9
+ *
10
+ * @deprecated Slated for removal in 1.0. Prefer `structuredContent` for
11
+ * machine-readable data. Both adapters still fall back to this field, but
12
+ * new tool code should not set it.
13
+ */
8
14
  details?: Record<string, unknown>;
9
15
  /** Tool-level error flag. Throw for unexpected adapter/runtime failures. */
10
16
  isError?: boolean;
11
17
  }
18
+ /**
19
+ * Discriminated union describing the shape `executePortableTool` puts into a
20
+ * failure result's `structuredContent`, and that the pi adapter exposes as
21
+ * `PortableToolExecutionError.details`.
22
+ *
23
+ * - `kind: "validation"` — TypeBox rejected the args. Always carries the
24
+ * offending tool name and the validation errors.
25
+ * - `kind: "domain"` — the tool's own handler returned `isError: true`. The
26
+ * shape of the rest of the object is whatever the handler chose to expose.
27
+ */
28
+ export type PortableToolErrorDetails = {
29
+ kind: "validation";
30
+ tool: string;
31
+ validationErrors: PortableValidationError[];
32
+ } | ({
33
+ kind: "domain";
34
+ } & Record<string, unknown>);
35
+ export interface PortableValidationError {
36
+ path: string;
37
+ message: string;
38
+ }
12
39
  export type PortableToolBuiltInHost = "pi" | "mcp" | "test";
13
40
  export type PortableToolHost<TExtension extends string = never> = PortableToolBuiltInHost | TExtension;
14
41
  export interface PortableToolContext<THost extends string = PortableToolBuiltInHost> {
@@ -1,15 +1,10 @@
1
1
  import type { TSchema } from "typebox";
2
- import type { PortableTool, PortableToolBuiltInHost, PortableToolContext, PortableToolResult } from "./define-tool.js";
3
- type NoInferPortable<T> = [T][T extends unknown ? 0 : never];
4
- export interface PortableValidationError {
5
- path: string;
6
- message: string;
7
- }
2
+ import type { PortableTool, PortableToolBuiltInHost, PortableToolContext, PortableToolResult, PortableValidationError } from "./define-tool.js";
3
+ export type { PortableValidationError };
8
4
  export declare function validatePortableToolArgs<THost extends string = PortableToolBuiltInHost>(tool: PortableTool<TSchema, THost>, args: unknown): {
9
5
  ok: true;
10
6
  } | {
11
7
  ok: false;
12
8
  errors: PortableValidationError[];
13
9
  };
14
- export declare function executePortableTool<THost extends string = PortableToolBuiltInHost>(tool: PortableTool<TSchema, THost>, args: unknown, ctx: PortableToolContext<NoInferPortable<THost>>): Promise<PortableToolResult>;
15
- export {};
10
+ export declare function executePortableTool<THost extends string = PortableToolBuiltInHost>(tool: PortableTool<TSchema, THost>, args: unknown, ctx: PortableToolContext<NoInfer<THost>>): Promise<PortableToolResult>;
@@ -19,6 +19,7 @@ export async function executePortableTool(tool, args, ctx) {
19
19
  .map((error) => `${error.path} ${error.message}`)
20
20
  .join("; ")}`,
21
21
  structuredContent: {
22
+ kind: "validation",
22
23
  tool: tool.name,
23
24
  validationErrors: validation.errors,
24
25
  },
@@ -1,2 +1,2 @@
1
- export { definePortableTool, type PortableTool, type PortableToolBuiltInHost, type PortableToolContext, type PortableToolHost, type PortableToolResult, } from "./core/define-tool.js";
1
+ export { definePortableTool, type PortableTool, type PortableToolBuiltInHost, type PortableToolContext, type PortableToolErrorDetails, type PortableToolHost, type PortableToolResult, } from "./core/define-tool.js";
2
2
  export { executePortableTool, type PortableValidationError, validatePortableToolArgs, } from "./core/execute-tool.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feniix/bridgekit",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "BridgeKit defines TypeBox-backed tools once and adapts them to pi, MCP, and other hosts.",
5
5
  "keywords": [
6
6
  "pi",
@@ -48,10 +48,9 @@
48
48
  "build": "npm run clean && tsc -b tsconfig.json",
49
49
  "test": "npm run build && node scripts/run-built-tests.mjs",
50
50
  "test:coverage": "npm run build && node scripts/run-built-tests-coverage.mjs",
51
- "verify:dist": "node scripts/verify-bridgekit-dist.mjs",
52
51
  "pack:dry-run": "npm pack --dry-run --json",
53
52
  "package-smoke": "node scripts/smoke-package.mjs",
54
- "prepack": "npm run build && npm run verify:dist"
53
+ "prepack": "npm run build"
55
54
  },
56
55
  "files": [
57
56
  "dist/**/*.js",