@feniix/bridgekit
Advanced tools
+39
-1
@@ -7,4 +7,42 @@ # Changelog | ||
| ## [0.9.5] - Unreleased | ||
| ## [0.10.0] - Unreleased | ||
| ### Removed (breaking) | ||
| - `PortableToolHost<TExtension extends string>` type alias — replaced by the | ||
| fixed `PortableToolBuiltInHost = "pi" | "mcp" | "test"` union. | ||
| - `<THost extends string>` generic parameter on `PortableTool`, | ||
| `PortableToolContext`, `definePortableTool`, `executePortableTool`, and | ||
| `validatePortableToolArgs`. Host is now a fixed literal union; adapters | ||
| outside `"pi" | "mcp"` should cast at their adapter boundary instead of | ||
| extending the type union. | ||
| Migration: drop the second generic argument from any of those types. | ||
| `PortableTool<typeof params, "custom">` becomes `PortableTool<typeof params>`. | ||
| `PortableToolContext<"custom">` becomes `PortableToolContext` (cast `host` | ||
| at the consumer if a custom literal is needed). | ||
| Cast pattern (custom literal at the adapter boundary): | ||
| ```ts | ||
| // PortableToolContext.host is fixed to PortableToolBuiltInHost. | ||
| // A direct `as "custom-runtime"` fails under strict (TS2352 — no overlap); | ||
| // cast through `unknown` instead. | ||
| const ctx: PortableToolContext = { | ||
| host: "custom-runtime" as unknown as PortableToolBuiltInHost, | ||
| }; | ||
| ``` | ||
| Caveat: the cast lies at the type-system boundary, so | ||
| `switch (ctx.host) { ... default: assertNever(ctx.host); }` patterns will | ||
| fall through on the custom literal at runtime. For custom dispatch, carry | ||
| the host identifier on an adapter-owned field rather than on `ctx.host`, | ||
| or do a runtime allowlist check before exhaustive narrowing. | ||
| No consumer in tree or in the three known downstream consumers | ||
| (pi-sequential-thinking, pi-exa, pi-code-reasoning) used the generic. | ||
| Resolves [#5](https://github.com/feniix/bridgekit/issues/5). | ||
| ## [0.9.5] - 2026-05-27 | ||
| ### Documentation | ||
@@ -11,0 +49,0 @@ |
@@ -40,5 +40,4 @@ import type { Static, TSchema } from "typebox"; | ||
| export type PortableToolBuiltInHost = "pi" | "mcp" | "test"; | ||
| export type PortableToolHost<TExtension extends string = never> = PortableToolBuiltInHost | TExtension; | ||
| export interface PortableToolContext<THost extends string = PortableToolBuiltInHost> { | ||
| host: THost; | ||
| export interface PortableToolContext { | ||
| host: PortableToolBuiltInHost; | ||
| signal?: AbortSignal; | ||
@@ -143,3 +142,3 @@ progress?: (update: PortableToolResult) => void; | ||
| } | ||
| export interface PortableTool<TParams extends TSchema = TSchema, THost extends string = PortableToolBuiltInHost> { | ||
| export interface PortableTool<TParams extends TSchema = TSchema> { | ||
| name: string; | ||
@@ -149,3 +148,3 @@ title: string; | ||
| parameters: TParams; | ||
| execute: (args: Static<TParams>, ctx: PortableToolContext<THost>) => PortableToolResult | Promise<PortableToolResult>; | ||
| execute: (args: Static<TParams>, ctx: PortableToolContext) => PortableToolResult | Promise<PortableToolResult>; | ||
| /** | ||
@@ -162,2 +161,2 @@ * Optional per-host metadata. Adapters consume the keys they recognise; | ||
| } | ||
| export declare function definePortableTool<TParams extends TSchema, THost extends string = PortableToolBuiltInHost>(tool: PortableTool<TParams, THost>): PortableTool<TParams, THost>; | ||
| export declare function definePortableTool<TParams extends TSchema>(tool: PortableTool<TParams>): PortableTool<TParams>; |
| import type { TSchema } from "typebox"; | ||
| import type { PortableTool, PortableToolBuiltInHost, PortableToolContext, PortableToolResult, PortableValidationError } from "./define-tool.js"; | ||
| export declare function validatePortableToolArgs<THost extends string = PortableToolBuiltInHost>(tool: PortableTool<TSchema, THost>, args: unknown): { | ||
| import type { PortableTool, PortableToolContext, PortableToolResult, PortableValidationError } from "./define-tool.js"; | ||
| export declare function validatePortableToolArgs(tool: PortableTool<TSchema>, args: unknown): { | ||
| ok: true; | ||
@@ -9,2 +9,2 @@ } | { | ||
| }; | ||
| export declare function executePortableTool<THost extends string = PortableToolBuiltInHost>(tool: PortableTool<TSchema, THost>, args: unknown, ctx: PortableToolContext<NoInfer<THost>>): Promise<PortableToolResult>; | ||
| export declare function executePortableTool(tool: PortableTool<TSchema>, args: unknown, ctx: PortableToolContext): Promise<PortableToolResult>; |
@@ -1,3 +0,3 @@ | ||
| export { definePortableTool, type McpHostExtras, type PiHostExtras, type PortableTool, type PortableToolBuiltInHost, type PortableToolContext, type PortableToolErrorDetails, type PortableToolHost, type PortableToolHostExtras, type PortableToolResult, type PortableValidationError, } from "./core/define-tool.js"; | ||
| export { definePortableTool, type McpHostExtras, type PiHostExtras, type PortableTool, type PortableToolBuiltInHost, type PortableToolContext, type PortableToolErrorDetails, type PortableToolHostExtras, type PortableToolResult, type PortableValidationError, } from "./core/define-tool.js"; | ||
| export { executePortableTool, validatePortableToolArgs } from "./core/execute-tool.js"; | ||
| export { isDomainFailure, isValidationFailure, type PortableDomainFailure, type PortableValidationFailure, } from "./core/result-guards.js"; |
+3
-49
@@ -286,50 +286,4 @@ # BridgeKit examples | ||
| ## 4. Use custom host typing for custom adapters | ||
| ## 4. Per-host metadata via `hostExtras` | ||
| Default portable tools accept only built-in hosts: `"pi" | "mcp" | "test"`. | ||
| If you are writing a custom adapter, opt in explicitly so the handler can safely narrow `ctx.host`: | ||
| ```ts | ||
| import { Type } from "typebox"; | ||
| import { | ||
| definePortableTool, | ||
| executePortableTool, | ||
| type PortableTool, | ||
| type PortableToolContext, | ||
| type PortableToolHost, | ||
| } from "@feniix/bridgekit"; | ||
| const params = Type.Object({ text: Type.String() }); | ||
| type CustomHost = "custom-runtime"; | ||
| type CustomTool = PortableTool<typeof params, CustomHost>; | ||
| const customTool = definePortableTool<typeof params, CustomHost>({ | ||
| name: "custom_echo", | ||
| title: "Custom Echo", | ||
| description: "Echoes text in a custom runtime.", | ||
| parameters: params, | ||
| execute(args, ctx) { | ||
| const host: CustomHost = ctx.host; | ||
| return { text: `${host}: ${args.text}` }; | ||
| }, | ||
| }); | ||
| async function runCustomTool(tool: CustomTool, text: string) { | ||
| const ctx: PortableToolContext<CustomHost> = { host: "custom-runtime" }; | ||
| return executePortableTool(tool, { text }, ctx); | ||
| } | ||
| const hostValue: PortableToolHost<CustomHost> = "custom-runtime"; | ||
| void hostValue; | ||
| void customTool; | ||
| ``` | ||
| Use `PortableToolHost<CustomHost>` for values that can be either a built-in host or your custom extension. Use `PortableToolContext<CustomHost>` or `PortableTool<Schema, CustomHost>` when a tool is custom-host-only. | ||
| --- | ||
| ## 5. Per-host metadata via `hostExtras` | ||
| When a tool needs host-specific metadata — pi's `pendingMessage` for a "Processing..." signal, MCP's annotations as advisory hints to clients — the canonical place is `PortableTool.hostExtras`. Each host has its own namespace; adapters read the keys they recognise and ignore the rest. Tools that omit `hostExtras` see no behavior change. | ||
@@ -370,3 +324,3 @@ | ||
| - Set only the fields the adapter recognises; unrecognised keys are silently ignored, but they add noise to the definition. | ||
| - For custom-host adapters, extend `PortableToolHostExtras` via `declare module "@feniix/bridgekit"` so consumers of your adapter get type safety on the new namespace. | ||
| - If you ship an adapter for a host outside `"pi" | "mcp"`, cast `ctx.host` at the adapter boundary (the host union is fixed since 0.10.0). For type-safe per-host metadata in your adapter's namespace, extend `PortableToolHostExtras` via `declare module "@feniix/bridgekit"` so consumers of your adapter get type safety on the new namespace. | ||
@@ -451,3 +405,3 @@ See `docs/rfc-host-extras.md` for the full design rationale (which fields qualify, why a top-level field beats a sidecar map, the closure rule for future additions). | ||
| ## 6. Package checklist | ||
| ## 5. Package checklist | ||
@@ -454,0 +408,0 @@ For publishable tool packages: |
+1
-18
@@ -9,3 +9,3 @@ # @feniix/bridgekit | ||
| 2. `llms.txt` — compact agent-facing usage rules and anti-patterns. | ||
| 3. `examples/README.md` — copyable end-to-end layouts for shared tools, pi extension wiring, MCP stdio server wiring, and custom hosts. | ||
| 3. `examples/README.md` — copyable end-to-end layouts for shared tools, pi extension wiring, MCP stdio server wiring, and per-host metadata via `hostExtras`. | ||
| 4. Published declarations such as `dist/src/index.d.ts`, `dist/src/pi.d.ts`, and `dist/src/mcp.d.ts` — canonical installed-package type contracts. In a source checkout, the matching `src/` files contain implementation context. | ||
@@ -100,19 +100,2 @@ | ||
| ## Custom host typing | ||
| Default tools accept only built-in hosts: `"pi" | "mcp" | "test"`. | ||
| For a custom adapter, opt in explicitly: | ||
| ```ts | ||
| const customTool = definePortableTool<typeof params, "custom-host">({ | ||
| // ... | ||
| execute(args, ctx) { | ||
| const host: "custom-host" = ctx.host; | ||
| return { text: host }; | ||
| }, | ||
| }); | ||
| ``` | ||
| Use `PortableToolHost<"custom-host">` when a value may be either a built-in host or that extension. | ||
| ## Mixed source-loaded hosts and compiled MCP bins | ||
@@ -119,0 +102,0 @@ |
+1
-1
| { | ||
| "name": "@feniix/bridgekit", | ||
| "version": "0.9.5", | ||
| "version": "0.10.0", | ||
| "description": "BridgeKit defines TypeBox-backed tools once and adapts them to pi, MCP, and other hosts.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
+2
-38
@@ -66,3 +66,3 @@ # BridgeKit | ||
| - Define a tool once; ship it to pi and MCP (and custom hosts) without per-host forks. | ||
| - Define a tool once; ship it to pi and MCP without per-host forks. | ||
| - Host-neutral tool files: no pi or MCP SDK imports in the tool definition itself. | ||
@@ -84,3 +84,2 @@ - TypeBox schemas pass through to MCP `inputSchema` directly — no JSON Schema conversion step. | ||
| type PortableToolContext, | ||
| type PortableToolHost, | ||
| type PortableToolResult, | ||
@@ -237,37 +236,2 @@ type PortableValidationError, | ||
| ### Custom host typing | ||
| Default portable tools accept the built-in host union: | ||
| ```ts | ||
| type BuiltIn = "pi" | "mcp" | "test"; | ||
| ``` | ||
| Custom adapters opt in explicitly: | ||
| ```ts | ||
| import { Type } from "typebox"; | ||
| import { definePortableTool, type PortableToolHost } from "@feniix/bridgekit"; | ||
| const params = Type.Object({ text: Type.String() }); | ||
| type CustomHost = "custom-runtime"; | ||
| export const customTool = definePortableTool<typeof params, CustomHost>({ | ||
| name: "custom_echo", | ||
| title: "Custom Echo", | ||
| description: "Echoes text in a custom runtime.", | ||
| parameters: params, | ||
| execute(args, ctx) { | ||
| const host: CustomHost = ctx.host; | ||
| return { text: `${host}: ${args.text}` }; | ||
| }, | ||
| }); | ||
| const hostValue: PortableToolHost<CustomHost> = "custom-runtime"; | ||
| void hostValue; | ||
| ``` | ||
| Use `PortableToolHost<CustomHost>` for values that may be either a built-in host or your extension. Use the `PortableTool`/`PortableToolContext` generic when a tool or adapter is custom-host-only. | ||
| ## Best practices | ||
@@ -312,3 +276,3 @@ | ||
| 2. `llms.txt` — compact agent-facing usage rules and anti-patterns. | ||
| 3. `examples/README.md` — copyable layouts for shared tools, pi extensions, MCP stdio servers, and custom hosts. | ||
| 3. `examples/README.md` — copyable layouts for shared tools, pi extensions, MCP stdio servers, and per-host metadata via `hostExtras`. | ||
| 4. Published declarations such as `dist/src/index.d.ts`, `dist/src/pi.d.ts`, and `dist/src/mcp.d.ts` — canonical installed-package type contracts. In a source checkout, the matching `src/` files contain the same implementation context. |
118597
-1.12%1195
-0.08%275
-11.58%